diff --git a/.env.docker b/.env.docker new file mode 100644 index 0000000000..cc4af2147d --- /dev/null +++ b/.env.docker @@ -0,0 +1,157 @@ +# -------------------------------------------- +# REQUIRED: DB SETUP +# -------------------------------------------- +MYSQL_DATABASE=snipeit +MYSQL_USER=snipeit +MYSQL_PASSWORD=changeme1234 +MYSQL_ROOT_PASSWORD=changeme1234 +# -------------------------------------------- +# REQUIRED: BASIC APP SETTINGS +# -------------------------------------------- +APP_ENV=develop +APP_DEBUG=false +# please regenerate the APP_KEY value by calling `docker-compose run --rm snipeit bash` and then `php artisan key:generate --show` and then copy paste the value here +APP_KEY=base64:3ilviXqB9u6DX1NRcyWGJ+sjySF+H18CPDGb3+IVwMQ= +APP_URL=http://localhost:8000 +APP_TIMEZONE='UTC' +APP_LOCALE=en +MAX_RESULTS=500 + +# -------------------------------------------- +# REQUIRED: UPLOADED FILE STORAGE SETTINGS +# -------------------------------------------- +PRIVATE_FILESYSTEM_DISK=local +PUBLIC_FILESYSTEM_DISK=local_public + +# -------------------------------------------- +# REQUIRED: DATABASE SETTINGS +# -------------------------------------------- +DB_CONNECTION=mysql +DB_HOST=mariadb +DB_DATABASE=snipeit +DB_USERNAME=snipeit +DB_PASSWORD=changeme1234 +DB_PREFIX=null +DB_DUMP_PATH='/usr/bin' +DB_CHARSET=utf8mb4 +DB_COLLATION=utf8mb4_unicode_ci + +# -------------------------------------------- +# OPTIONAL: SSL DATABASE SETTINGS +# -------------------------------------------- +DB_SSL=false +DB_SSL_IS_PAAS=false +DB_SSL_KEY_PATH=null +DB_SSL_CERT_PATH=null +DB_SSL_CA_PATH=null +DB_SSL_CIPHER=null + +# -------------------------------------------- +# REQUIRED: OUTGOING MAIL SERVER SETTINGS +# -------------------------------------------- +MAIL_DRIVER=smtp +MAIL_HOST=mailhog +MAIL_PORT=1025 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDR=you@example.com +MAIL_FROM_NAME='Snipe-IT' +MAIL_REPLYTO_ADDR=you@example.com +MAIL_REPLYTO_NAME='Snipe-IT' +MAIL_AUTO_EMBED_METHOD='attachment' + +# -------------------------------------------- +# REQUIRED: IMAGE LIBRARY +# This should be gd or imagick +# -------------------------------------------- +IMAGE_LIB=gd + + +# -------------------------------------------- +# OPTIONAL: BACKUP SETTINGS +# -------------------------------------------- +MAIL_BACKUP_NOTIFICATION_DRIVER=null +MAIL_BACKUP_NOTIFICATION_ADDRESS=null +BACKUP_ENV=true + + +# -------------------------------------------- +# OPTIONAL: SESSION SETTINGS +# -------------------------------------------- +SESSION_LIFETIME=12000 +EXPIRE_ON_CLOSE=false +ENCRYPT=false +COOKIE_NAME=snipeit_session +COOKIE_DOMAIN=null +SECURE_COOKIES=false +API_TOKEN_EXPIRATION_YEARS=40 + +# -------------------------------------------- +# OPTIONAL: SECURITY HEADER SETTINGS +# -------------------------------------------- +APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1 +ALLOW_IFRAMING=false +REFERRER_POLICY=same-origin +ENABLE_CSP=false +CORS_ALLOWED_ORIGINS=null +ENABLE_HSTS=false + +# -------------------------------------------- +# OPTIONAL: CACHE SETTINGS +# -------------------------------------------- +CACHE_DRIVER=file +SESSION_DRIVER=file +QUEUE_DRIVER=sync +CACHE_PREFIX=snipeit + +# -------------------------------------------- +# OPTIONAL: REDIS SETTINGS +# -------------------------------------------- +REDIS_HOST=redis +REDIS_PASSWORD=null +REDIS_PORT=6379 + +# -------------------------------------------- +# OPTIONAL: MEMCACHED SETTINGS +# -------------------------------------------- +MEMCACHED_HOST=null +MEMCACHED_PORT=null + +# -------------------------------------------- +# OPTIONAL: PUBLIC S3 Settings +# -------------------------------------------- +PUBLIC_AWS_SECRET_ACCESS_KEY=null +PUBLIC_AWS_ACCESS_KEY_ID=null +PUBLIC_AWS_DEFAULT_REGION=null +PUBLIC_AWS_BUCKET=null +PUBLIC_AWS_URL=null +PUBLIC_AWS_BUCKET_ROOT=null + +# -------------------------------------------- +# OPTIONAL: PRIVATE S3 Settings +# -------------------------------------------- +PRIVATE_AWS_ACCESS_KEY_ID=null +PRIVATE_AWS_SECRET_ACCESS_KEY=null +PRIVATE_AWS_DEFAULT_REGION=null +PRIVATE_AWS_BUCKET=null +PRIVATE_AWS_URL=null +PRIVATE_AWS_BUCKET_ROOT=null + +# -------------------------------------------- +# OPTIONAL: LOGIN THROTTLING +# -------------------------------------------- +LOGIN_MAX_ATTEMPTS=5 +LOGIN_LOCKOUT_DURATION=60 +RESET_PASSWORD_LINK_EXPIRES=900 + +# -------------------------------------------- +# OPTIONAL: MISC +# -------------------------------------------- +APP_LOG=stderr +APP_LOG_MAX_FILES=10 +APP_LOCKED=false +APP_CIPHER=AES-256-CBC +GOOGLE_MAPS_API= +LDAP_MEM_LIM=500M +LDAP_TIME_LIM=600 diff --git a/.github/workflows/codacy-analysis.yml b/.github/workflows/codacy-analysis.yml new file mode 100644 index 0000000000..bbc5bc09d9 --- /dev/null +++ b/.github/workflows/codacy-analysis.yml @@ -0,0 +1,49 @@ +# This workflow checks out code, performs a Codacy security scan +# and integrates the results with the +# GitHub Advanced Security code scanning feature. For more information on +# the Codacy security scan action usage and parameters, see +# https://github.com/codacy/codacy-analysis-cli-action. +# For more information on Codacy Analysis CLI in general, see +# https://github.com/codacy/codacy-analysis-cli. + +name: Codacy Security Scan + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '36 23 * * 3' + +jobs: + codacy-security-scan: + name: Codacy Security Scan + runs-on: ubuntu-latest + steps: + # Checkout the repository to the GitHub Actions runner + - name: Checkout code + uses: actions/checkout@v2 + + # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis + - name: Run Codacy Analysis CLI + uses: codacy/codacy-analysis-cli-action@1.1.0 + with: + # Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository + # You can also omit the token and run the tools that support default configurations + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + verbose: true + output: results.sarif + format: sarif + # Adjust severity of non-security issues + gh-code-scanning-compat: true + # Force 0 exit code to allow SARIF file generation + # This will handover control about PR rejection to the GitHub side + max-allowed-issues: 2147483647 + + # Upload the SARIF file generated in the previous step + - name: Upload SARIF results file + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index e47e773fd0..9771de971b 100755 --- a/.gitignore +++ b/.gitignore @@ -48,7 +48,7 @@ tests/_support/_generated/* /npm-debug.log /storage/oauth-private.key /storage/oauth-public.key - +logs/* *.cache .vagrant diff --git a/Dockerfile.fpm-alpine b/Dockerfile.fpm-alpine new file mode 100644 index 0000000000..3452a6f89b --- /dev/null +++ b/Dockerfile.fpm-alpine @@ -0,0 +1,102 @@ +ARG ENVIRONMENT=production +ARG SNIPEIT_RELEASE=5.1.3 +ARG PHP_VERSION=7.4.16 +ARG PHP_ALPINE_VERSION=3.13 +ARG COMPOSER_VERSION=2.0.11 + +# Cannot use arguments with 'COPY --from' workaround +# https://github.com/moby/moby/issues/34482#issuecomment-454716952 +FROM composer:${COMPOSER_VERSION} AS composer + +# Final stage +FROM php:${PHP_VERSION}-fpm-alpine${PHP_ALPINE_VERSION} AS source +LABEL maintainer="Mateus Villar " + +ARG PACKAGES="\ + mysql-client \ +" +ARG DEV_PACKAGES="\ + git \ +" +ARG ENVIRONMENT +ENV ENVIRONMENT ${ENVIRONMENT} +ARG SNIPEIT_RELEASE +ENV SNIPEIT_RELEASE ${SNIPEIT_RELEASE} + +# Cribbed from wordpress-fpm-alpine image +# set recommended PHP.ini settings +# see https://secure.php.net/manual/en/opcache.installation.php +RUN set -eux; \ + docker-php-ext-enable opcache; \ + { \ + echo 'opcache.memory_consumption=128'; \ + echo 'opcache.interned_strings_buffer=8'; \ + echo 'opcache.max_accelerated_files=4000'; \ + echo 'opcache.revalidate_freq=2'; \ + echo 'opcache.fast_shutdown=1'; \ + } > /usr/local/etc/php/conf.d/opcache-recommended.ini +# https://wordpress.org/support/article/editing-wp-config-php/#configure-error-logging +RUN { \ +# https://www.php.net/manual/en/errorfunc.constants.php +# https://github.com/docker-library/wordpress/issues/420#issuecomment-517839670 + echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \ + echo 'display_errors = Off'; \ + echo 'display_startup_errors = Off'; \ + echo 'log_errors = On'; \ + echo 'error_log = /dev/stderr'; \ + echo 'log_errors_max_len = 1024'; \ + echo 'ignore_repeated_errors = On'; \ + echo 'ignore_repeated_source = Off'; \ + echo 'html_errors = Off'; \ + } > /usr/local/etc/php/conf.d/error-logging.ini + +# Install php extensions inside docker containers easily +# https://github.com/mlocati/docker-php-extension-installer +COPY --from=mlocati/php-extension-installer:1.2.19 /usr/bin/install-php-extensions /usr/local/bin/ +RUN set -eux; \ + install-php-extensions \ + bcmath \ + gd \ + ldap \ + mysqli \ + pdo_mysql \ + zip; \ + rm -f /usr/local/bin/install-php-extensions; \ +# Install prerequisites packages + apk add --no-cache \ + ${PACKAGES}; + +COPY --from=composer /usr/bin/composer /usr/local/bin +ARG COMPOSER_ALLOW_SUPERUSER=1 +RUN set -eux; \ +# Download and extract snipeit tarball + curl -o snipeit.tar.gz -fL "https://github.com/snipe/snipe-it/archive/v$SNIPEIT_RELEASE.tar.gz"; \ + tar -xzf snipeit.tar.gz --strip-components=1 -C /var/www/html/; \ + rm snipeit.tar.gz; \ +# Install composer php dependencies + if [ "$ENVIRONMENT" = "production" ]; then \ + echo "production enviroment detected!"; \ + composer update \ + --no-cache \ + --no-dev \ + --optimize-autoloader \ + --working-dir=/var/www/html; \ + else \ + echo "development enviroment detected!"; \ + apk add --no-cache \ + ${DEV_PACKAGES}; \ + composer update \ + --no-cache \ + --prefer-source \ + --optimize-autoloader \ + --working-dir=/var/www/html; \ + fi; \ + rm -f /usr/local/bin/composer; \ + chown -R www-data:www-data /var/www/html; + +VOLUME [ "/var/lib/snipeit" ] + +COPY --chown=www-data:www-data docker/docker-secrets.env /var/www/html/.env +COPY --chmod=655 docker/docker-entrypoint.sh /usr/local/bin/docker-snipeit-entrypoint +ENTRYPOINT [ "/usr/local/bin/docker-snipeit-entrypoint" ] +CMD [ "/usr/local/bin/docker-php-entrypoint", "php-fpm" ] \ No newline at end of file diff --git a/app/Http/Controllers/Assets/AssetsController.php b/app/Http/Controllers/Assets/AssetsController.php index 52b880186c..12c5f06949 100755 --- a/app/Http/Controllers/Assets/AssetsController.php +++ b/app/Http/Controllers/Assets/AssetsController.php @@ -572,7 +572,12 @@ class AssetsController extends Controller * * This needs a LOT of love. It's done very inelegantly right now, and there are * a ton of optimizations that could (and should) be done. - * + * + * Updated to respect checkin dates: + * No checkin column, assume all items are checked in (todays date) + * Checkin date in the past, update history. + * Checkin date in future or empty, check the item out to the user. + * * @author [A. Gianotto] [] * @since [v3.3] * @return View @@ -589,6 +594,8 @@ class AssetsController extends Controller } $csv = Reader::createFromPath($request->file('user_import_csv')); $csv->setHeaderOffset(0); + $header = $csv->getHeader(); + $isCheckinHeaderExplicit = in_array("checkin date", (array_map('strtolower', $header))); $results = $csv->getRecords(); $item = array(); $status = array(); @@ -603,8 +610,19 @@ class AssetsController extends Controller } $batch_counter = count($item[$asset_tag]); $item[$asset_tag][$batch_counter]['checkout_date'] = Carbon::parse(Helper::array_smart_fetch($row, "checkout date"))->format('Y-m-d H:i:s'); - $item[$asset_tag][$batch_counter]['checkin_date'] = Carbon::parse(Helper::array_smart_fetch($row, "checkin date"))->format('Y-m-d H:i:s'); - \Log::debug($item[$asset_tag][$batch_counter]['checkin_date']); + + if ($isCheckinHeaderExplicit){ + //checkin date not empty, assume past transaction or future checkin date (expected) + if (!empty(Helper::array_smart_fetch($row, "checkin date"))) { + $item[$asset_tag][$batch_counter]['checkin_date'] = Carbon::parse(Helper::array_smart_fetch($row, "checkin date"))->format('Y-m-d H:i:s'); + } else { + $item[$asset_tag][$batch_counter]['checkin_date'] = ''; + } + } else { + //checkin header missing, assume data is unavailable and make checkin date explicit (now) so we don't encounter invalid state. + $item[$asset_tag][$batch_counter]['checkin_date'] = Carbon::parse(now())->format('Y-m-d H:i:s'); + } + $item[$asset_tag][$batch_counter]['asset_tag'] = Helper::array_smart_fetch($row, "asset tag"); $item[$asset_tag][$batch_counter]['name'] = Helper::array_smart_fetch($row, "name"); $item[$asset_tag][$batch_counter]['email'] = Helper::array_smart_fetch($row, "email"); @@ -632,16 +650,24 @@ class AssetsController extends Controller $user_query .= ', or on username '.$firstname['username']; } if ($request->input('match_email')=='1') { - if ($item[$asset_tag][$batch_counter]['email']=='') { + if ($item[$asset_tag][$batch_counter]['name']=='') { $item[$asset_tag][$batch_counter]['username'][] = $user_email = User::generateEmailFromFullName($item[$asset_tag][$batch_counter]['name']); $user->orWhere('username', '=', $user_email); $user_query .= ', or on username '.$user_email; } } + if ($request->input('match_username') == '1'){ + // Added #8825: add explicit username lookup + $raw_username = $item[$asset_tag][$batch_counter]['name']; + $user->orWhere('username', '=', $raw_username); + $user_query .= ', or on username ' . $raw_username; + } + // A matching user was found if ($user = $user->first()) { - $item[$asset_tag][$batch_counter]['checkedout_to'] = $user->id; + //$user is now matched user from db $item[$asset_tag][$batch_counter]['user_id'] = $user->id; + Actionlog::firstOrCreate(array( 'item_id' => $asset->id, 'item_type' => Asset::class, @@ -652,14 +678,44 @@ class AssetsController extends Controller 'created_at' => $item[$asset_tag][$batch_counter]['checkout_date'], 'action_type' => 'checkout', )); - $asset->assigned_to = $user->id; + + $checkin_date = $item[$asset_tag][$batch_counter]['checkin_date']; + + if ($isCheckinHeaderExplicit) { + + //if checkin date header exists, assume that empty or future date is still checked out + //if checkin is before todays date, assume it's checked in and do not assign user ID, if checkin date is in the future or blank, this is the expected checkin date, items is checked out + + if ((strtotime($checkin_date) > strtotime(Carbon::now())) || (empty($checkin_date)) + ) { + //only do this if item is checked out + $asset->assigned_to = $user->id; + $asset->assigned_type = User::class; + } + } + + if (!empty($checkin_date)) { + //only make a checkin there is a valid checkin date or we created one on import. + Actionlog::firstOrCreate(array( + 'item_id' => + $item[$asset_tag][$batch_counter]['asset_id'], + 'item_type' => Asset::class, + 'user_id' => Auth::user()->id, + 'note' => 'Checkin imported by ' . Auth::user()->present()->fullName() . ' from history importer', + 'target_id' => null, + 'created_at' => $checkin_date, + 'action_type' => 'checkin' + )); + + } + if ($asset->save()) { $status['success'][]['asset'][$asset_tag]['msg'] = 'Asset successfully matched for '.Helper::array_smart_fetch($row, "name").$user_query.' on '.$item[$asset_tag][$batch_counter]['checkout_date']; } else { $status['error'][]['asset'][$asset_tag]['msg'] = 'Asset and user was matched but could not be saved.'; } } else { - $item[$asset_tag][$batch_counter]['checkedout_to'] = null; + $item[$asset_tag][$batch_counter]['user_id'] = null; $status['error'][]['user'][Helper::array_smart_fetch($row, "name")]['msg'] = 'User does not exist so no checkin log was created.'; } } else { @@ -668,31 +724,6 @@ class AssetsController extends Controller } } } - // Loop through and backfill the checkins - foreach ($item as $key => $asset_batch) { - $total_in_batch = count($asset_batch); - for ($x = 0; $x < $total_in_batch; $x++) { - $next = $x + 1; - // Only do this if a matching user was found - if ((array_key_exists('checkedout_to', $asset_batch[$x])) && ($asset_batch[$x]['checkedout_to']!='')) { - if (($total_in_batch > 1) && ($x < $total_in_batch) && (array_key_exists($next, $asset_batch))) { - $checkin_date = Carbon::parse($asset_batch[$next]['checkin_date'])->format('Y-m-d H:i:s'); - $asset_batch[$x]['real_checkin'] = $checkin_date; - \Log::debug($asset_batch[$next]['checkin_date']); - \Log::debug($checkin_date); - Actionlog::firstOrCreate(array( - 'item_id' => $asset_batch[$x]['asset_id'], - 'item_type' => Asset::class, - 'user_id' => Auth::user()->id, - 'note' => 'Checkin imported by ' . Auth::user()->present()->fullName() . ' from history importer', - 'target_id' => null, - 'created_at' => $checkin_date, - 'action_type' => 'checkin' - )); - } - } - } - } return view('hardware/history')->with('status', $status); } diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 1dd8f1f150..58c07ed23c 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -140,8 +140,9 @@ class LoginController extends Controller * * @throws \Exception */ - private function loginViaLdap(LdapAd $ldap, Request $request): User + private function loginViaLdap(Request $request): User { + $ldap = \App::make( LdapAd::class); try { return $ldap->ldapLogin($request->input('username'), $request->input('password')); } catch (\Exception $ex) { diff --git a/app/Http/Controllers/CustomFieldsController.php b/app/Http/Controllers/CustomFieldsController.php index e58e00c193..a8be424841 100644 --- a/app/Http/Controllers/CustomFieldsController.php +++ b/app/Http/Controllers/CustomFieldsController.php @@ -40,6 +40,24 @@ class CustomFieldsController extends Controller } + /** + * Just redirect the user back if they try to view the details of a field. + * We already show those details on the listing page. + * + * @see CustomFieldsController::storeField() + * @author [A. Gianotto] [] + * @since [v5.1.5] + * @return Redirect + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + + public function show() + { + return redirect()->route("fields.index"); + + } + + /** * Returns a view with a form to create a new custom field. * @@ -133,16 +151,19 @@ class CustomFieldsController extends Controller */ public function destroy($field_id) { - $field = CustomField::find($field_id); + if ($field = CustomField::find($field_id)) { - $this->authorize('delete', $field); + $this->authorize('delete', $field); - if ($field->fieldset->count()>0) { - return redirect()->back()->withErrors(['message' => "Field is in-use"]); + if (($field->fieldset) && ($field->fieldset->count() > 0)) { + return redirect()->back()->withErrors(['message' => "Field is in-use"]); + } + $field->delete(); + return redirect()->route("fields.index") + ->with("success", trans('admin/custom_fields/message.field.delete.success')); } - $field->delete(); - return redirect()->route("fields.index") - ->with("success", trans('admin/custom_fields/message.field.delete.success')); + + return redirect()->back()->withErrors(['message' => "Field does not exist"]); } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..511dfd35d7 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,48 @@ +version: '3' + +services: + snipeit: + build: + context: . + dockerfile: Dockerfile.alpine + container_name: snipeit + ports: + - 8000:80 + volumes: + - ./logs:/var/www/html/storage/logs + depends_on: + - mariadb + - redis + env_file: + - .env.docker + networks: + - snipeit-backend + + mariadb: + image: mariadb:latest + volumes: + - db:/var/lib/mysql + env_file: + - .env.docker + networks: + - snipeit-backend + + redis: + image: redis:latest + networks: + - snipeit-backend + + mailhog: + image: mailhog/mailhog:latest + ports: + # - 1025:1025 + - 8025:8025 + networks: + - snipeit-backend + + +volumes: + db: {} + +networks: + snipeit-backend: {} \ No newline at end of file diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh new file mode 100755 index 0000000000..3b783419eb --- /dev/null +++ b/docker/docker-entrypoint.sh @@ -0,0 +1,119 @@ +#!/bin/sh + +set -eo pipefail; + +# Cribbed from nextcloud docker official repo +# https://github.com/nextcloud/docker/blob/master/docker-entrypoint.sh +# usage: file_env VAR [DEFAULT] +# ie: file_env 'XYZ_DB_PASSWORD' 'example' +# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of +# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) +file_env() { + local var="$1" + local fileVar="${var}_FILE" + local def="${2:-}" + local varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//") + local fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//") + if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then + echo >&2 "error: both $var and $fileVar are set (but are exclusive)" + exit 1 + fi + if [ -n "${varValue}" ]; then + export "$var"="${varValue}" + elif [ -n "${fileVarValue}" ]; then + export "$var"="$(cat "${fileVarValue}")" + elif [ -n "${def}" ]; then + export "$var"="$def" + fi + unset "$fileVar" +} + +# Add docker secrets support for the variables below: +file_env APP_KEY +file_env DB_HOST +file_env DB_PORT +file_env DB_DATABASE +file_env DB_USERNAME +file_env DB_PASSWORD +file_env REDIS_HOST +file_env REDIS_PASSWORD +file_env REDIS_PORT +file_env MAIL_HOST +file_env MAIL_PORT +file_env MAIL_USERNAME +file_env MAIL_PASSWORD + +echo [INFO docker entrypoint] Start script execution + +# Generate new app key if none is provided +if [ -z "$APP_KEY" -a -z "$APP_KEY_FILE" ] +then + echo "Please re-run this container with an environment variable \$APP_KEY" + echo "An example APP_KEY you could use is: " + php artisan key:generate --show + exit +fi + +# Directory configuration +rm -rf \ + "/var/www/html/storage/private_uploads" \ + "/var/www/html/public/uploads" \ + "/var/www/html/storage/app/backups" + +# Create data directories +for dir in \ + 'data/private_uploads' \ + 'data/uploads/accessories' \ + 'data/uploads/avatars' \ + 'data/uploads/barcodes' \ + 'data/uploads/categories' \ + 'data/uploads/companies' \ + 'data/uploads/components' \ + 'data/uploads/consumables' \ + 'data/uploads/departments' \ + 'data/uploads/locations' \ + 'data/uploads/manufacturers' \ + 'data/uploads/models' \ + 'data/uploads/suppliers' \ + 'dumps' \ + 'keys' +do + [ ! -d "/var/lib/snipeit/$dir" ] && mkdir -p "/var/lib/snipeit/$dir" +done + +# Sync /var/lib/snipeit (docker volume) with /var/www/html directory +ln -fs \ + "/var/lib/snipeit/data/private_uploads" "/var/www/html/storage/private_uploads" +ln -fs \ + "/var/lib/snipeit/data/uploads" "/var/www/html/public/uploads" +ln -fs \ + "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups" +ln -fs \ + "/var/lib/snipeit/keys/oauth-public.key" "/var/www/html/storage/oauth-public.key" +ln -fs \ + "/var/lib/snipeit/keys/oauth-private.key" "/var/www/html/storage/oauth-private.key" + +# If the Oauth DB files are not present copy the vendor files over to the db migrations +if [ ! -f "/var/www/html/database/migrations/*create_oauth*" ] +then + cp -a /var/www/html/vendor/laravel/passport/database/migrations/* /var/www/html/database/migrations/ +fi + +# Create laravel log file +touch /var/www/html/storage/logs/laravel.log +# Add correct permissions for files and directories +chown www-data:www-data /var/www/html/storage/logs/laravel.log +chown -R www-data:www-data \ + /var/lib/snipeit/data \ + /var/lib/snipeit/dumps \ + /var/lib/snipeit/keys + +# Migrate/create database +php artisan migrate --force +# Clear cache files +php artisan config:clear +php artisan config:cache + +echo [INFO docker entrypoint] End script execution + +exec "$@" \ No newline at end of file diff --git a/docker/docker-secrets.env b/docker/docker-secrets.env new file mode 100644 index 0000000000..45a777f5d8 --- /dev/null +++ b/docker/docker-secrets.env @@ -0,0 +1,54 @@ +# -------------------------------------------- +# REQUIRED: BASIC APP SETTINGS +# -------------------------------------------- +#APP_ENV=develop +#APP_DEBUG=false +#APP_KEY=Change_this_key_or_snipe_will_get_ya +#APP_URL=http://127.0.0.1:32782 +#APP_TIMEZONE=US/Pacific +#APP_LOCALE=en + + +# -------------------------------------------- +# REQUIRED: DATABASE SETTINGS +# -------------------------------------------- +DB_CONNECTION=mysql +DB_PREFIX=null +DB_DUMP_PATH='/usr/bin' + + +# -------------------------------------------- +# REQUIRED: OUTGOING MAIL SERVER SETTINGS +# -------------------------------------------- +MAIL_DRIVER=smtp +MAIL_ENCRYPTION=${MAIL_ENV_ENCRYPTION} +MAIL_FROM_ADDR=${MAIL_ENV_FROM_ADDR} +MAIL_FROM_NAME=${MAIL_ENV_FROM_NAME} +MAIL_REPLYTO_ADDR=${MAIL_ENV_FROM_ADDR} +MAIL_REPLYTO_NAME=${MAIL_ENV_FROM_NAME} + + +# -------------------------------------------- +# REQUIRED: IMAGE LIBRARY +# This should be gd or imagick +# -------------------------------------------- +IMAGE_LIB=gd + + +# -------------------------------------------- +# OPTIONAL: SESSION SETTINGS +# -------------------------------------------- +SESSION_LIFETIME=12000 +EXPIRE_ON_CLOSE=false +ENCRYPT=false +COOKIE_NAME=snipeit_session +COOKIE_DOMAIN=null +SECURE_COOKIES=false + + +# -------------------------------------------- +# OPTIONAL: CACHE SETTINGS +# -------------------------------------------- +CACHE_DRIVER=file +SESSION_DRIVER=file +QUEUE_DRIVER=sync diff --git a/package-lock.json b/package-lock.json index 27d698551c..32384ba3b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7030,6 +7030,12 @@ "omggif": "1.0.7", "promise-polyfill": "8.1.0", "stackblur-canvas": "2.2.0" + }, + "dependencies": { + "file-saver": { + "version": "git+ssh://git@github.com/eligrey/FileSaver.js.git#e865e37af9f9947ddcced76b549e27dc45c1cb2e", + "from": "git+ssh://git@github.com/eligrey/FileSaver.js.git#e865e37af9f9947ddcced76b549e27dc45c1cb2e" + } } }, "stackblur-canvas": { @@ -8887,7 +8893,8 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true + "dev": true, + "optional": true }, "pify": { "version": "2.3.0", @@ -12214,9 +12221,9 @@ "dev": true }, "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", "dev": true, "requires": { "figgy-pudding": "^3.5.1" diff --git a/resources/views/categories/index.blade.php b/resources/views/categories/index.blade.php index 08f995f65b..32610f910c 100755 --- a/resources/views/categories/index.blade.php +++ b/resources/views/categories/index.blade.php @@ -2,7 +2,7 @@ {{-- Page title --}} @section('title') -{{ trans('admin/categories/general.asset_categories') }} +{{ trans('general.categories') }} @parent @stop diff --git a/resources/views/custom_fields/index.blade.php b/resources/views/custom_fields/index.blade.php index e5b038260f..50b5a64f27 100644 --- a/resources/views/custom_fields/index.blade.php +++ b/resources/views/custom_fields/index.blade.php @@ -1,4 +1,8 @@ -@extends('layouts/default') +@extends('layouts/default', [ + 'helpText' => trans('admin/custom_fields/general.about_fieldsets_text'), + 'helpPosition' => 'right', +]) + {{-- Page title --}} @section('title') @@ -10,7 +14,7 @@ @can('view', \App\Models\CustomFieldset::class)
-
+
@@ -83,12 +87,9 @@
-
- -
-

{{ trans('admin/custom_fields/general.about_fieldsets_title') }}

-

{{ trans('admin/custom_fields/general.about_fieldsets_text') }}

-
+
+ +
@endcan @can('view', \App\Models\CustomField::class) diff --git a/resources/views/hardware/history.blade.php b/resources/views/hardware/history.blade.php index 328ad8d78f..17a02b883a 100644 --- a/resources/views/hardware/history.blade.php +++ b/resources/views/hardware/history.blade.php @@ -64,10 +64,9 @@ Upload a CSV that contains asset history. The assets and users MUST already exist in the system, or they will be skipped. Matching assets for history import happens against the asset tag. We will try to find a matching user based on the user's name you provide, and the criteria you select below. If you do not select any criteria below, it will simply try to match on the username format you configured in the Admin > General Settings.

-

Fields included in the CSV must match the headers: Asset Tag, Checkout Date, Checkin Date, Name. Any additional fields will be ignored.

+

Fields included in the CSV must match the headers: Asset Tag, Name, Checkout Date, Checkin Date. Any additional fields will be ignored.

- -

History should be ordered by date in ascending order.

+

Checkin Date: blank or future checkin dates will checkout items to associated user. Excluding the Checkin Date column will create a checkin date with todays date.

@@ -118,6 +117,14 @@
+ +
+
+
+
+ {{ Form::checkbox('match_username', '1', Request::old('match_username')) }} Try to match users by username +
+
diff --git a/resources/views/statuslabels/index.blade.php b/resources/views/statuslabels/index.blade.php index 17ec83e9b2..39c7ce2a19 100755 --- a/resources/views/statuslabels/index.blade.php +++ b/resources/views/statuslabels/index.blade.php @@ -1,4 +1,8 @@ -@extends('layouts/default') +@extends('layouts/default', [ + 'helpText' => trans('admin/statuslabels/table.info') , + 'helpPosition' => 'right', +]) + {{-- Page title --}} @section('title') @@ -60,7 +64,6 @@

{{ trans('admin/statuslabels/table.about') }}

-

{{ trans('admin/statuslabels/table.info') }}