From e8ba9d78d9eeb34e34211d093dc9763563ffbe3b Mon Sep 17 00:00:00 2001 From: sujith-mn Date: Tue, 1 Nov 2022 22:03:57 +0530 Subject: [PATCH 1/2] jpa example --- integration/spring_jpa/.gitignore | 33 ++ .../spring_jpa/.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 58727 bytes .../.mvn/wrapper/maven-wrapper.properties | 2 + integration/spring_jpa/README.adoc | 31 ++ integration/spring_jpa/mvnw | 316 ++++++++++++++++++ integration/spring_jpa/mvnw.cmd | 188 +++++++++++ integration/spring_jpa/pom.xml | 80 +++++ .../springjpa/SpringJpaApplication.java | 13 + .../springjpa/domain/model/BookingEntity.java | 45 +++ .../springjpa/domain/model/BookingStatus.java | 22 ++ .../domain/model/InvitedGuestEntity.java | 22 ++ .../converter/BookingStatusConverter.java | 46 +++ .../domain/repository/BookingRepository.java | 11 + .../model/ApplicationPersistenceEntity.java | 28 ++ .../src/main/resources/application.properties | 11 + .../migration/V0001__R002_Create_schema.sql | 27 ++ .../db/migration/V0002__R002_Master_data.sql | 31 ++ .../springjpa/BookingRepositoryTest.java | 111 ++++++ 18 files changed, 1017 insertions(+) create mode 100644 integration/spring_jpa/.gitignore create mode 100644 integration/spring_jpa/.mvn/wrapper/maven-wrapper.jar create mode 100644 integration/spring_jpa/.mvn/wrapper/maven-wrapper.properties create mode 100644 integration/spring_jpa/README.adoc create mode 100644 integration/spring_jpa/mvnw create mode 100644 integration/spring_jpa/mvnw.cmd create mode 100644 integration/spring_jpa/pom.xml create mode 100644 integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/SpringJpaApplication.java create mode 100644 integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/BookingEntity.java create mode 100644 integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/BookingStatus.java create mode 100644 integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/InvitedGuestEntity.java create mode 100644 integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/converter/BookingStatusConverter.java create mode 100644 integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/repository/BookingRepository.java create mode 100644 integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/general/domain/model/ApplicationPersistenceEntity.java create mode 100644 integration/spring_jpa/src/main/resources/application.properties create mode 100644 integration/spring_jpa/src/main/resources/db/migration/V0001__R002_Create_schema.sql create mode 100644 integration/spring_jpa/src/main/resources/db/migration/V0002__R002_Master_data.sql create mode 100644 integration/spring_jpa/src/test/java/com/devonfw/examples/dataaccess/springjpa/BookingRepositoryTest.java diff --git a/integration/spring_jpa/.gitignore b/integration/spring_jpa/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/integration/spring_jpa/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/integration/spring_jpa/.mvn/wrapper/maven-wrapper.jar b/integration/spring_jpa/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..c1dd12f17644411d6e840bd5a10c6ecda0175f18 GIT binary patch literal 58727 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%0ANsA|A3-jj|r`KAo%oTajX3>^E` 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 YM{}PByk10KD_nuQ4E7Du?}+~TKh4V)`~Uy| literal 0 HcmV?d00001 diff --git a/integration/spring_jpa/.mvn/wrapper/maven-wrapper.properties b/integration/spring_jpa/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..b74bf7f --- /dev/null +++ b/integration/spring_jpa/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/integration/spring_jpa/README.adoc b/integration/spring_jpa/README.adoc new file mode 100644 index 0000000..1e1aa00 --- /dev/null +++ b/integration/spring_jpa/README.adoc @@ -0,0 +1,31 @@ += JPA Example + +Examples prepared to explain the below concepts. + +1. Use Lazy loading +2. Usage of java.time.localdate. +3. Enum usage with converter + + +=== Lazy loading + +Always advised to use lazy loading. Lazy loading fetches data only when it is really required. This example uses FetchType Lazy, the invited guests will be loaded only on demand. + +A `SELECT` query applied for fetching booking or bookings will not fetch any invited guests. The below statement fetches all guests for a particular booking. It executes a `SELECT` query with booking id as arguement. + `BookingEntity.getInvitedGuests()` + + +=== Enum + +Using Converter annotation convert an entity attribute to a database value and vice versa. In this example, enum defined as BOOKED("B"), and NOTBOOKED("N") and CANCELED ("C"). This helps to make sure the code is not broken by refactorings. + +The other two ways to deal with enums are by their `Ordinal` or by `name`. `Ordinal` may not be a good choice, if someone changes the places of the items in enum, wrong integer value may be stored. + +=== LocalDateTime + +java.time.LocalDateTime used instead of java.util.date, major benefits are: + +* LocalDateTime API has its own format/parse methods. +* Addition/subtraction operation are easier (minusMinutes,plusDays etc.) + + diff --git a/integration/spring_jpa/mvnw b/integration/spring_jpa/mvnw new file mode 100644 index 0000000..8a8fb22 --- /dev/null +++ b/integration/spring_jpa/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/integration/spring_jpa/mvnw.cmd b/integration/spring_jpa/mvnw.cmd new file mode 100644 index 0000000..1d8ab01 --- /dev/null +++ b/integration/spring_jpa/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/integration/spring_jpa/pom.xml b/integration/spring_jpa/pom.xml new file mode 100644 index 0000000..b62d5e0 --- /dev/null +++ b/integration/spring_jpa/pom.xml @@ -0,0 +1,80 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.3 + + + com.devonfw.examples.dataaccess.springjpa + spring_jpa + 0.0.1-SNAPSHOT + spring_jpa + devonfw jpa example (lazy loading, timestamp, enums) + + 11 + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + javax.persistence + javax.persistence-api + + + org.springframework.data + spring-data-jpa + + + + + com.h2database + h2 + + + + + org.flywaydb + flyway-core + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + diff --git a/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/SpringJpaApplication.java b/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/SpringJpaApplication.java new file mode 100644 index 0000000..63ea02d --- /dev/null +++ b/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/SpringJpaApplication.java @@ -0,0 +1,13 @@ +package com.devonfw.examples.dataaccess.springjpa; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringJpaApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringJpaApplication.class, args); + } + +} diff --git a/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/BookingEntity.java b/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/BookingEntity.java new file mode 100644 index 0000000..af2d139 --- /dev/null +++ b/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/BookingEntity.java @@ -0,0 +1,45 @@ +package com.devonfw.examples.dataaccess.springjpa.domain.model; + +import static javax.persistence.CascadeType.ALL; + +import java.time.LocalDateTime; +import java.util.Set; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +import org.hibernate.annotations.BatchSize; + +import com.devonfw.examples.dataaccess.springjpa.general.domain.model.ApplicationPersistenceEntity; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Setter +@Getter +@ToString(callSuper = true, includeFieldNames = true) +@Entity +@Table (name="booking") +public class BookingEntity extends ApplicationPersistenceEntity { + + private String name; + + private String bookingToken; + + private String comment; + + private LocalDateTime bookingDate; + + private String email; + + private BookingStatus bookingStatus; + + @OneToMany(cascade = ALL, fetch = FetchType.LAZY, orphanRemoval = true) + @JoinColumn(name = "id_booking") + @BatchSize(size = 2) + private Set invitedGuests; +} diff --git a/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/BookingStatus.java b/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/BookingStatus.java new file mode 100644 index 0000000..a5c5f57 --- /dev/null +++ b/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/BookingStatus.java @@ -0,0 +1,22 @@ +package com.devonfw.examples.dataaccess.springjpa.domain.model; + +/** + * BookingStatus enumerator helps to handle Status in a better way. Here the + * code associated with each enum is persisted to the database instead of the + * whole string, by the BookingStatusConverter. + */ +public enum BookingStatus { + BOOKED("B"), CANCELED("C"), NOTBOOKED("N"); + + private String code; + + BookingStatus(String code) { + + this.code = code; + } + + public String getCode() { + + return this.code; + } +} diff --git a/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/InvitedGuestEntity.java b/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/InvitedGuestEntity.java new file mode 100644 index 0000000..31f90c9 --- /dev/null +++ b/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/InvitedGuestEntity.java @@ -0,0 +1,22 @@ +package com.devonfw.examples.dataaccess.springjpa.domain.model; + +import javax.persistence.Entity; +import javax.persistence.Table; + +import com.devonfw.examples.dataaccess.springjpa.general.domain.model.ApplicationPersistenceEntity; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +@Setter +@Getter +@ToString(callSuper = true, includeFieldNames = true) +@Entity +@Table (name="Invited_Guest") +public class InvitedGuestEntity extends ApplicationPersistenceEntity { + + private String guestToken; + + private String email; + +} diff --git a/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/converter/BookingStatusConverter.java b/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/converter/BookingStatusConverter.java new file mode 100644 index 0000000..b3aeefe --- /dev/null +++ b/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/model/converter/BookingStatusConverter.java @@ -0,0 +1,46 @@ +package com.devonfw.examples.dataaccess.springjpa.domain.model.converter; + +import java.util.stream.Stream; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +import com.devonfw.examples.dataaccess.springjpa.domain.model.BookingStatus; +/** + * BookingStatusConverter defines a mapping between BookinStatus enum and the + * database column booking_status at booking table. + * + */ +@Converter(autoApply = true) +public class BookingStatusConverter + implements + AttributeConverter { + + /** + * Converts BookingStatus to string 'code', eg: `BOOKED` to 'B'. + */ + @Override + public String convertToDatabaseColumn(BookingStatus status) { + + if (status == null) { + return null; + } + return status.getCode(); + } + + /** + * Converts the string 'code' to BookingStatus eg: 'B' to `BOOKED` + */ + @Override + public BookingStatus convertToEntityAttribute(final String code) { + + if (code == null) { + return null; + } + + return Stream.of(BookingStatus.values()) + .filter(c -> c.getCode().equals(code)).findFirst() + .orElseThrow(IllegalArgumentException::new); + } + +} diff --git a/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/repository/BookingRepository.java b/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/repository/BookingRepository.java new file mode 100644 index 0000000..9af33ce --- /dev/null +++ b/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/domain/repository/BookingRepository.java @@ -0,0 +1,11 @@ +package com.devonfw.examples.dataaccess.springjpa.domain.repository; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import com.devonfw.examples.dataaccess.springjpa.domain.model.BookingEntity; + +@Repository +public interface BookingRepository extends CrudRepository { + +} diff --git a/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/general/domain/model/ApplicationPersistenceEntity.java b/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/general/domain/model/ApplicationPersistenceEntity.java new file mode 100644 index 0000000..e4fa679 --- /dev/null +++ b/integration/spring_jpa/src/main/java/com/devonfw/examples/dataaccess/springjpa/general/domain/model/ApplicationPersistenceEntity.java @@ -0,0 +1,28 @@ +package com.devonfw.examples.dataaccess.springjpa.general.domain.model; + +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; +import javax.persistence.Version; + +import lombok.Getter; +import lombok.Setter; + +/** + * Abstract Entity for all Entities with an id and a version field. + * + */ + +@Setter +@Getter +@MappedSuperclass +public abstract class ApplicationPersistenceEntity { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private Long id; + + @Version + private Integer modificationCounter; +} \ No newline at end of file diff --git a/integration/spring_jpa/src/main/resources/application.properties b/integration/spring_jpa/src/main/resources/application.properties new file mode 100644 index 0000000..8ae5125 --- /dev/null +++ b/integration/spring_jpa/src/main/resources/application.properties @@ -0,0 +1,11 @@ +logging.level.org.hibernate.SQL=DEBUG +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.format_sql=true +logging.level.org.hibernate.type=DEBUG + +logging.level.org.hibernate.type.descriptor.sql.BasicBinder=DEBUG + +# Flyway for Database Setup and Migrations +spring.flyway.enabled=true +spring.flyway.clean-on-validation-error=true +spring.flyway.locations=classpath:db/migration \ No newline at end of file diff --git a/integration/spring_jpa/src/main/resources/db/migration/V0001__R002_Create_schema.sql b/integration/spring_jpa/src/main/resources/db/migration/V0001__R002_Create_schema.sql new file mode 100644 index 0000000..45d28bb --- /dev/null +++ b/integration/spring_jpa/src/main/resources/db/migration/V0001__R002_Create_schema.sql @@ -0,0 +1,27 @@ +CREATE SEQUENCE HIBERNATE_SEQUENCE START WITH 100000; + + +CREATE TABLE Booking ( + id BIGINT NOT NULL AUTO_INCREMENT, + modification_counter INTEGER NOT NULL, + id_user BIGINT, + name VARCHAR (255) NOT NULL, + booking_token VARCHAR (255), + comment VARCHAR (4000), + email VARCHAR(255) NOT NULL, + booking_date TIMESTAMP NOT NULL, + expiration_date TIMESTAMP, + booking_status VARCHAR(1), + CONSTRAINT PK_Booking PRIMARY KEY(id) +); + +CREATE TABLE Invited_Guest ( + id BIGINT NOT NULL AUTO_INCREMENT, + modification_counter INTEGER NOT NULL, + id_booking BIGINT , + guest_token VARCHAR (255), + email VARCHAR (60), + accepted BOOLEAN, + CONSTRAINT PK_InvitedGuest PRIMARY KEY(id), + CONSTRAINT FK_InvitedGuest_idBooking FOREIGN KEY(id_booking) REFERENCES Booking(id) NOCHECK +); diff --git a/integration/spring_jpa/src/main/resources/db/migration/V0002__R002_Master_data.sql b/integration/spring_jpa/src/main/resources/db/migration/V0002__R002_Master_data.sql new file mode 100644 index 0000000..c9c3f80 --- /dev/null +++ b/integration/spring_jpa/src/main/resources/db/migration/V0002__R002_Master_data.sql @@ -0,0 +1,31 @@ +INSERT INTO booking( + id, modification_counter, id_user, + NAME, booking_token, comment, email, + booking_date, expiration_date, + booking_status +) +VALUES + ( + 0, 0, 0, + 'user0', 'CB_20170509_123502555Z', 'Booking Type CSR', 'user0@mail.com', + Dateadd('DAY', 5, CURRENT_TIMESTAMP), + Dateadd( + 'DAY', + 5, + Dateadd('HOUR', -1, CURRENT_TIMESTAMP) + ), + 'C' + ); +INSERT INTO Invited_Guest( + id, modification_counter, id_booking, + guest_token, email, accepted +) +VALUES + ( + 0, + 0, + 0, + 'GB_20170510_02350266501Z', + 'guest0@mail.com', + true + ); \ No newline at end of file diff --git a/integration/spring_jpa/src/test/java/com/devonfw/examples/dataaccess/springjpa/BookingRepositoryTest.java b/integration/spring_jpa/src/test/java/com/devonfw/examples/dataaccess/springjpa/BookingRepositoryTest.java new file mode 100644 index 0000000..947c71b --- /dev/null +++ b/integration/spring_jpa/src/test/java/com/devonfw/examples/dataaccess/springjpa/BookingRepositoryTest.java @@ -0,0 +1,111 @@ +package com.devonfw.examples.dataaccess.springjpa; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.LocalDateTime; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import javax.transaction.Transactional; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import com.devonfw.examples.dataaccess.springjpa.domain.model.BookingEntity; +import com.devonfw.examples.dataaccess.springjpa.domain.model.BookingStatus; +import com.devonfw.examples.dataaccess.springjpa.domain.model.InvitedGuestEntity; +import com.devonfw.examples.dataaccess.springjpa.domain.repository.BookingRepository; + +/** + * Unit test prepared to test the following features: + * + * LocalDateTime - BookingDate is time.LocalDateTime instead of util.Date + * + * Enum (using converter) - BookingStatus is assigned BOOKED but + * the database stores only the code 'B'. To verify actual value (code) from the database, applying native + * query to retrieve row data. + * + * Lazy loading - Lazy loading verified by looking at queries applied against database from the logs. + * While applying findById executes query to fetch data from booking table not querying against invited + * guests. But when we verify `getInvitedGuests().size()` the invited guests query executes. + * + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest +public class BookingRepositoryTest { + + @Autowired + private BookingRepository bookingRepository; + + @PersistenceContext + private EntityManager em; + + @Test + @Transactional + public void givenBooking_whenSave_thenGetOk() { + + // Booking + Invited Guests creation + BookingEntity newBooking = getBooking("booking1"); + BookingEntity booking = this.bookingRepository.save(newBooking); + + // Retrieving the booking: + Optional foundBooking = this.bookingRepository + .findById(booking.getId()); + if (foundBooking.isPresent()) { + booking = foundBooking.get(); + assertEquals(booking.getName(), newBooking.getName()); + assertEquals(booking.getBookingDate(), newBooking.getBookingDate()); + assertEquals(booking.getInvitedGuests().size(), 10); + } + + // Native query used here to get the exact Enum data from the DB. + + Query q = this.em.createNativeQuery( + "SELECT b.name,b.booking_status FROM Booking b where id=" + + booking.getId()); + List bookings = q.getResultList(); + assertEquals(bookings.size(), 1); + for (Object[] bookingObject : bookings) { + assertEquals(bookingObject[0], newBooking.getName()); + assertEquals(bookingObject[1], BookingStatus.BOOKED.getCode()); + } + + } + + private BookingEntity getBooking(String bookingName) { + + BookingEntity booking = new BookingEntity(); + booking.setName(bookingName); + booking.setBookingStatus(BookingStatus.BOOKED); + booking.setBookingDate(LocalDateTime.now()); + booking.setName(bookingName); + booking.setBookingToken("token"); + booking.setComment("comment"); + booking.setEmail(bookingName + "@gmail.com"); + booking.setInvitedGuests(getInvitedGuest(bookingName)); + return booking; + } + + private Set getInvitedGuest(String bookingName) { + + Set guests = new HashSet<>(); + for (int i = 0; i < 10; i++) { + InvitedGuestEntity guest = new InvitedGuestEntity(); + guest.setGuestToken( + String.format("%s-guest%stoken", bookingName, i)); + guest.setEmail( + String.format("%s.user%s@gmail.com", bookingName, i)); + guests.add(guest); + } + return guests; + } + +} From 7692452077764d678f5f552deb6aab06faed1731 Mon Sep 17 00:00:00 2001 From: sujith-mn Date: Tue, 1 Nov 2022 22:08:58 +0530 Subject: [PATCH 2/2] keycloak example --- .../quarkus-keycloak-authorization/.classpath | 43 + .../.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 59925 bytes .../.mvn/wrapper/maven-wrapper.properties | 18 + .../quarkus-keycloak-authorization/.project | 23 + .../org.eclipse.core.resources.prefs | 7 + .../.settings/org.eclipse.jdt.core.prefs | 9 + .../.settings/org.eclipse.m2e.core.prefs | 4 + .../README.adoc | 13 + .../config/keycloak-keystore.jks | Bin 0 -> 2228 bytes .../config/quarkus-realm.json | 1659 +++++++++++++++++ .../docker-compose.yaml | 18 + .../quarkus-keycloak-authorization/mvnw | 316 ++++ .../quarkus-keycloak-authorization/mvnw.cmd | 188 ++ .../quarkus-keycloak-authorization/pom.xml | 312 ++++ .../src/main/docker/Dockerfile.jvm | 54 + .../src/main/docker/Dockerfile.legacy-jar | 51 + .../src/main/docker/Dockerfile.native | 27 + .../src/main/docker/Dockerfile.native-micro | 23 + .../com/devonfw/quarkus/DemoApplication.java | 17 + .../model/ApplicationPersistenceEntity.java | 79 + .../model/ApplicationSearchCriteriaDto.java | 23 + .../domain/model/ProductEntity.java | 26 + .../domain/repo/ProductRepository.java | 16 + .../rest/v1/ProductReadService.java | 85 + .../rest/v1/ProductWriteService.java | 81 + .../rest/v1/mapper/ProductMapper.java | 26 + .../rest/v1/model/AbstractDto.java | 14 + .../rest/v1/model/ProductDto.java | 29 + .../v1/model/ProductSearchCriteriaDto.java | 25 + .../productmanagement/rest/v1/model/User.java | 18 + .../productmanagement/utils/QueryUtil.java | 32 + .../productmanagement/utils/StringUtils.java | 8 + .../src/main/resources/application.properties | 26 + .../db/migration/V001__Create_schema.sql | 15 + .../db/migration/V002__Master_data.sql | 352 ++++ .../authorization/NativePolicyEnforcerIT.java | 10 + .../authorization/PolicyEnforcerTest.java | 40 + 37 files changed, 3687 insertions(+) create mode 100644 integration/quarkus-keycloak-authorization/.classpath create mode 100644 integration/quarkus-keycloak-authorization/.mvn/wrapper/maven-wrapper.jar create mode 100644 integration/quarkus-keycloak-authorization/.mvn/wrapper/maven-wrapper.properties create mode 100644 integration/quarkus-keycloak-authorization/.project create mode 100644 integration/quarkus-keycloak-authorization/.settings/org.eclipse.core.resources.prefs create mode 100644 integration/quarkus-keycloak-authorization/.settings/org.eclipse.jdt.core.prefs create mode 100644 integration/quarkus-keycloak-authorization/.settings/org.eclipse.m2e.core.prefs create mode 100644 integration/quarkus-keycloak-authorization/README.adoc create mode 100644 integration/quarkus-keycloak-authorization/config/keycloak-keystore.jks create mode 100644 integration/quarkus-keycloak-authorization/config/quarkus-realm.json create mode 100644 integration/quarkus-keycloak-authorization/docker-compose.yaml create mode 100644 integration/quarkus-keycloak-authorization/mvnw create mode 100644 integration/quarkus-keycloak-authorization/mvnw.cmd create mode 100644 integration/quarkus-keycloak-authorization/pom.xml create mode 100644 integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.jvm create mode 100644 integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.legacy-jar create mode 100644 integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.native create mode 100644 integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.native-micro create mode 100644 integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/DemoApplication.java create mode 100644 integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/general/domain/model/ApplicationPersistenceEntity.java create mode 100644 integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/general/domain/model/ApplicationSearchCriteriaDto.java create mode 100644 integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/domain/model/ProductEntity.java create mode 100644 integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/domain/repo/ProductRepository.java create mode 100644 integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/ProductReadService.java create mode 100644 integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/ProductWriteService.java create mode 100644 integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/mapper/ProductMapper.java create mode 100644 integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/AbstractDto.java create mode 100644 integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/ProductDto.java create mode 100644 integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/ProductSearchCriteriaDto.java create mode 100644 integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/User.java create mode 100644 integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/utils/QueryUtil.java create mode 100644 integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/utils/StringUtils.java create mode 100644 integration/quarkus-keycloak-authorization/src/main/resources/application.properties create mode 100644 integration/quarkus-keycloak-authorization/src/main/resources/db/migration/V001__Create_schema.sql create mode 100644 integration/quarkus-keycloak-authorization/src/main/resources/db/migration/V002__Master_data.sql create mode 100644 integration/quarkus-keycloak-authorization/src/test/java/com/devonfw/quarkus/keycloak/authorization/NativePolicyEnforcerIT.java create mode 100644 integration/quarkus-keycloak-authorization/src/test/java/com/devonfw/quarkus/keycloak/authorization/PolicyEnforcerTest.java diff --git a/integration/quarkus-keycloak-authorization/.classpath b/integration/quarkus-keycloak-authorization/.classpath new file mode 100644 index 0000000..2881385 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/.classpath @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integration/quarkus-keycloak-authorization/.mvn/wrapper/maven-wrapper.jar b/integration/quarkus-keycloak-authorization/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..bf82ff01c6cdae4a1bb754a6e062954d77ac5c11 GIT binary patch literal 59925 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

dy!05UMhSKsK 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 VCNKd3JGtOvEm(5t0lFyE{{i--k}m)N literal 0 HcmV?d00001 diff --git a/integration/quarkus-keycloak-authorization/.mvn/wrapper/maven-wrapper.properties b/integration/quarkus-keycloak-authorization/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..dc3affc --- /dev/null +++ b/integration/quarkus-keycloak-authorization/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar diff --git a/integration/quarkus-keycloak-authorization/.project b/integration/quarkus-keycloak-authorization/.project new file mode 100644 index 0000000..a465598 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/.project @@ -0,0 +1,23 @@ + + + security-keycloak-authorization-quickstart + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/integration/quarkus-keycloak-authorization/.settings/org.eclipse.core.resources.prefs b/integration/quarkus-keycloak-authorization/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..4641f22 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/main/resources=UTF-8 +encoding//src/test/java=UTF-8 +encoding//target/generated-sources/annotations=UTF-8 +encoding/=UTF-8 +encoding/config=UTF-8 diff --git a/integration/quarkus-keycloak-authorization/.settings/org.eclipse.jdt.core.prefs b/integration/quarkus-keycloak-authorization/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..55e549b --- /dev/null +++ b/integration/quarkus-keycloak-authorization/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,9 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.methodParameters=generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=11 diff --git a/integration/quarkus-keycloak-authorization/.settings/org.eclipse.m2e.core.prefs b/integration/quarkus-keycloak-authorization/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/integration/quarkus-keycloak-authorization/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/integration/quarkus-keycloak-authorization/README.adoc b/integration/quarkus-keycloak-authorization/README.adoc new file mode 100644 index 0000000..3c651ba --- /dev/null +++ b/integration/quarkus-keycloak-authorization/README.adoc @@ -0,0 +1,13 @@ +# Using Keycloak Authorization Services and Policy Enforcer to Protect JAX-RS Applications + + +Start postgres DB: + +`docker compose up` + +Run the application in dev mode: + +`mvn quarkus:dev` + + + diff --git a/integration/quarkus-keycloak-authorization/config/keycloak-keystore.jks b/integration/quarkus-keycloak-authorization/config/keycloak-keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..6961a6b1d02030386c861968cf3ac3ae87699190 GIT binary patch literal 2228 zcmchY`9IW+7RTp1n~)L0WUQg5;+gqcCNzyvWGPD`CZxu`Z!^SWZDb-_wkLa1L`Zfa zyCIRSnAybP4&1rHVYAX`962M( z0GL)pF18%_IA;?w$vnEnNI)(B7D?doCrQ^n$;(iZPzHIe;r5nRY!FBCP?F2!%<7A@ zsw`P{V|hOZLI3`S^gEgp71g|3$9f`CWT%2VD#LRLs6Wv{tQS27#8n;QiYgx-aOe(J zu!6Hq-Y?}|;3~=#t!V7q1U4Jwh3fh`f#{O%K$l$~F;qB7VR0zLLZsjh!E;a4JfH8Y z#pb;KRyjK=rg?^cMR7#+bYat`PRjMrj%=E4M^7#l36%z|tqG^n_LWQ2W>EdwlV6stY1^(n&D; zJ}39A9^wG~k4VGA9TXcnLEWjSEr8{c*sIsmgiEW$S%OR2 z3QfZM{WI_`0H(o9Qzpiy<{OcTH_ z!LyH_OZUXQ>Z@yb=zHcQwJ)^^d;k6NXfR0JKejNxj=l+vaQ&FNc=Jkq^`r)~y=Lb` z!CjZoJxu6~*;1>9*4;j?^zLHum=C)uI$2D?j$(;CE{h%wT{{`S-LhNvCv$q!DK;rv9*f>4bDIJw1)u&$?Xy^x(Y zUBUd~%+9IzMZ2Sffp3GMYy81g@t5kbb{K_v(T<_OYZ zwjdpyZO=NvpFZbEOZO*Kl?_m zHOZN(_3CD?|7owO?08TqL$8P^?DVYb(moE!dED9p?0$mt>fD>2TN#bWqlunWxSz4b zjq4%vDAb!xKh11Dr+U-lQTULRh%O^OQF z>)w`VG*!Q+nE!6{cFfilRD>AZjkhwHq8+f}2AV3bnynaB4c7&U!J^ko*12ce*97|0 zCwSZ6i+h)K$I6%*e|OH@O2m8mh7-iq%xi*Km&ojMx*JZf|2%Vm>7QGkJJmQ2)$UWs z-o!RXiIUgV4Zy5j(>eLQ^A;&o7*Re}k*0W_TGu{0ZGs&;EhF!hiP3FI*orNo*vpA? z&g9k4L%4wG!e?4k5+;(5wccLO#tgxscS)xL!LmEOA95tQ+Hq)$ literal 0 HcmV?d00001 diff --git a/integration/quarkus-keycloak-authorization/config/quarkus-realm.json b/integration/quarkus-keycloak-authorization/config/quarkus-realm.json new file mode 100644 index 0000000..4d78ab7 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/config/quarkus-realm.json @@ -0,0 +1,1659 @@ +{ + "id" : "11d78bf6-6d10-4484-baba-a1388379d68b", + "realm" : "quarkus", + "notBefore" : 0, + "revokeRefreshToken" : false, + "refreshTokenMaxReuse" : 0, + "accessTokenLifespan" : 300, + "accessTokenLifespanForImplicitFlow" : 900, + "ssoSessionIdleTimeout" : 1800, + "ssoSessionMaxLifespan" : 36000, + "ssoSessionIdleTimeoutRememberMe" : 0, + "ssoSessionMaxLifespanRememberMe" : 0, + "offlineSessionIdleTimeout" : 2592000, + "offlineSessionMaxLifespanEnabled" : false, + "offlineSessionMaxLifespan" : 5184000, + "accessCodeLifespan" : 60, + "accessCodeLifespanUserAction" : 300, + "accessCodeLifespanLogin" : 1800, + "actionTokenGeneratedByAdminLifespan" : 43200, + "actionTokenGeneratedByUserLifespan" : 300, + "enabled" : true, + "sslRequired" : "external", + "registrationAllowed" : false, + "registrationEmailAsUsername" : false, + "rememberMe" : false, + "verifyEmail" : false, + "loginWithEmailAllowed" : true, + "duplicateEmailsAllowed" : false, + "resetPasswordAllowed" : false, + "editUsernameAllowed" : false, + "bruteForceProtected" : false, + "permanentLockout" : false, + "maxFailureWaitSeconds" : 900, + "minimumQuickLoginWaitSeconds" : 60, + "waitIncrementSeconds" : 60, + "quickLoginCheckMilliSeconds" : 1000, + "maxDeltaTimeSeconds" : 43200, + "failureFactor" : 30, + "roles" : { + "realm" : [ { + "id" : "3fc80564-13ac-4e7b-9986-322f571e82bc", + "name" : "confidential", + "composite" : false, + "clientRole" : false, + "containerId" : "11d78bf6-6d10-4484-baba-a1388379d68b", + "attributes" : { } + }, { + "id" : "39eb64c8-66a9-4983-9c81-27ea7e2f6273", + "name" : "uma_authorization", + "description" : "${role_uma_authorization}", + "composite" : false, + "clientRole" : false, + "containerId" : "11d78bf6-6d10-4484-baba-a1388379d68b", + "attributes" : { } + }, { + "id" : "8c1abe12-62fe-4a06-ae0d-f5fb67dddbb0", + "name" : "admin", + "composite" : false, + "clientRole" : false, + "containerId" : "11d78bf6-6d10-4484-baba-a1388379d68b", + "attributes" : { } + }, { + "id" : "5afce544-6a3c-495f-b805-fd737cf5081e", + "name" : "user", + "composite" : false, + "clientRole" : false, + "containerId" : "11d78bf6-6d10-4484-baba-a1388379d68b", + "attributes" : { } + }, { + "id" : "bc431d62-a80a-425b-961a-0fb3fc59006d", + "name" : "offline_access", + "description" : "${role_offline-access}", + "composite" : false, + "clientRole" : false, + "containerId" : "11d78bf6-6d10-4484-baba-a1388379d68b", + "attributes" : { } + } ], + "client" : { + "realm-management" : [ { + "id" : "7db1f38d-d436-4725-93fd-030a3bbe628e", + "name" : "manage-identity-providers", + "description" : "${role_manage-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "1163b9bd-7319-4154-a25f-0101b2548d21", + "name" : "impersonation", + "description" : "${role_impersonation}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "73d0a556-072b-404f-bf8e-10e2544c8c27", + "name" : "view-identity-providers", + "description" : "${role_view-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "7e727e28-2095-4443-b2da-865e684f2308", + "name" : "view-realm", + "description" : "${role_view-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "df9e5352-f835-4467-bcaf-cb1b5f55c1ec", + "name" : "query-users", + "description" : "${role_query-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "fa77909a-32a3-41ae-9983-2b92ae03080c", + "name" : "manage-clients", + "description" : "${role_manage-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "a8780507-dc72-4433-8b95-b8e4f3c37d0e", + "name" : "manage-events", + "description" : "${role_manage-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "f7f4697a-3977-42f6-af86-9bb006cf4d04", + "name" : "realm-admin", + "description" : "${role_realm-admin}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "impersonation", "manage-identity-providers", "view-identity-providers", "view-realm", "query-users", "manage-clients", "manage-events", "manage-realm", "view-authorization", "manage-authorization", "view-users", "create-client", "query-clients", "query-groups", "manage-users", "view-clients", "view-events", "query-realms" ] + } + }, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "ca7dc1ce-a981-4efe-b3f0-a7192b6d3943", + "name" : "manage-realm", + "description" : "${role_manage-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "a0ab4faa-00a9-4f52-ac9f-8e764b6a8126", + "name" : "view-authorization", + "description" : "${role_view-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "0b4ed5e0-eceb-4d81-ba05-fa67022abe59", + "name" : "manage-authorization", + "description" : "${role_manage-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "c10336be-06f3-40ef-bef5-28d8c9b8a1e2", + "name" : "create-client", + "description" : "${role_create-client}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "1a1ffadc-11d5-44ea-bac0-d94372c8ae5c", + "name" : "view-users", + "description" : "${role_view-users}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-groups", "query-users" ] + } + }, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "5ba9a1a3-9027-4531-8253-b91f6058513c", + "name" : "query-clients", + "description" : "${role_query-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "b4fba807-7a7e-4e3e-bd31-45703305a9e3", + "name" : "query-groups", + "description" : "${role_query-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "c9384254-0af3-434c-b4ed-7c94f59a8247", + "name" : "manage-users", + "description" : "${role_manage-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "9a0022f2-bd58-4418-828c-a8e7abe3346b", + "name" : "view-clients", + "description" : "${role_view-clients}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-clients" ] + } + }, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "83df8311-4366-4d22-9425-eccc343faa3f", + "name" : "view-events", + "description" : "${role_view-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + }, { + "id" : "e81bf277-047f-4bdd-afd6-59e2016c5066", + "name" : "query-realms", + "description" : "${role_query-realms}", + "composite" : false, + "clientRole" : true, + "containerId" : "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes" : { } + } ], + "security-admin-console" : [ ], + "admin-cli" : [ ], + "backend-service" : [ { + "id" : "df147a91-6da7-4bbc-866c-f30cf99b2637", + "name" : "uma_protection", + "composite" : false, + "clientRole" : true, + "containerId" : "0ac5df91-e044-4051-bd03-106a3a5fb9cc", + "attributes" : { } + } ], + "broker" : [ { + "id" : "d36865b0-7ade-4bcd-a7dc-1dacbd80f169", + "name" : "read-token", + "description" : "${role_read-token}", + "composite" : false, + "clientRole" : true, + "containerId" : "53d4fe53-a039-471e-886a-28eddc950e95", + "attributes" : { } + } ], + "account" : [ { + "id" : "539325a0-d9b3-4821-97ee-d42999296b62", + "name" : "view-profile", + "description" : "${role_view-profile}", + "composite" : false, + "clientRole" : true, + "containerId" : "e55e1234-38fa-432d-8d90-39f5e024688d", + "attributes" : { } + }, { + "id" : "e4af836c-c884-4a57-8b1d-fb673b0fe3a5", + "name" : "manage-account", + "description" : "${role_manage-account}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "manage-account-links" ] + } + }, + "clientRole" : true, + "containerId" : "e55e1234-38fa-432d-8d90-39f5e024688d", + "attributes" : { } + }, { + "id" : "35d1c998-bcae-4ab1-a026-4c67bff49a98", + "name" : "manage-account-links", + "description" : "${role_manage-account-links}", + "composite" : false, + "clientRole" : true, + "containerId" : "e55e1234-38fa-432d-8d90-39f5e024688d", + "attributes" : { } + } ] + } + }, + "groups" : [ ], + "defaultRoles" : [ "uma_authorization", "offline_access" ], + "requiredCredentials" : [ "password" ], + "otpPolicyType" : "totp", + "otpPolicyAlgorithm" : "HmacSHA1", + "otpPolicyInitialCounter" : 0, + "otpPolicyDigits" : 6, + "otpPolicyLookAheadWindow" : 1, + "otpPolicyPeriod" : 30, + "otpSupportedApplications" : [ "FreeOTP", "Google Authenticator" ], + "scopeMappings" : [ { + "clientScope" : "offline_access", + "roles" : [ "offline_access" ] + } ], + "clients" : [ { + "id" : "e55e1234-38fa-432d-8d90-39f5e024688d", + "clientId" : "account", + "name" : "${client_account}", + "baseUrl" : "/auth/realms/quarkus/account", + "surrogateAuthRequired" : false, + "enabled" : true, + "clientAuthenticatorType" : "client-secret", + "secret" : "0136c3ef-0dfd-4b13-a6d0-2c8b6358edec", + "defaultRoles" : [ "view-profile", "manage-account" ], + "redirectUris" : [ "/auth/realms/quarkus/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "role_list", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "e9cc41a2-8e35-4d5e-949e-4879880c2ddb", + "clientId" : "admin-cli", + "name" : "${client_admin-cli}", + "surrogateAuthRequired" : false, + "enabled" : true, + "clientAuthenticatorType" : "client-secret", + "secret" : "a951803a-79c7-46a6-8197-e32835286971", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : false, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "role_list", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "53d4fe53-a039-471e-886a-28eddc950e95", + "clientId" : "broker", + "name" : "${client_broker}", + "surrogateAuthRequired" : false, + "enabled" : true, + "clientAuthenticatorType" : "client-secret", + "secret" : "e1f7edd7-e15c-43b4-8736-ff8204d16836", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "role_list", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "0ac5df91-e044-4051-bd03-106a3a5fb9cc", + "clientId" : "backend-service", + "surrogateAuthRequired" : false, + "enabled" : true, + "clientAuthenticatorType" : "client-secret", + "secret" : "secret", + "redirectUris" : ["*"], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : true, + "authorizationServicesEnabled" : true, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : -1, + "protocolMappers" : [ { + "id" : "3eac903f-c16b-4a78-a7e8-eb8f4d402b71", + "name" : "Client ID", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientId", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientId", + "jsonType.label" : "String" + } + }, { + "id" : "8422cefe-7f42-4f3b-abad-5f06f7d4b748", + "name" : "Client IP Address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientAddress", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientAddress", + "jsonType.label" : "String" + } + }, { + "id" : "988e47d6-2055-45eb-82d6-0b8b25c629fc", + "name" : "Client Host", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientHost", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientHost", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "web-origins", "role_list", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ], + "authorizationSettings" : { + "allowRemoteResourceManagement": true, + "policyEnforcementMode": "ENFORCING", + "resources": [ + { + "name": "User Resource", + "ownerManagedAccess": false, + "attributes": {}, + "_id": "df1b74a9-3f10-499d-a581-368de48e512b", + "uris": [ + "/product/v1/users/*" + ] + }, + { + "name": "Administration Resource", + "ownerManagedAccess": false, + "attributes": {}, + "_id": "7124e2f1-e6dc-44b4-87ab-24b010090b97", + "uris": [ + "/product/v1/admin/*" + ] + } + ], + "policies": [ + { + "id": "b8710fa6-160e-4de0-adf3-398c7007a0af", + "name": "Any User Policy", + "description": "Any user granted with the user role can access something", + "type": "role", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "roles": "[{\"id\":\"user\",\"required\":false}]" + } + }, + { + "id": "fcef30b2-68b2-4b78-9f3d-9162c6cdf5cb", + "name": "Only Administrators", + "description": "Only administrators can access", + "type": "role", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "roles": "[{\"id\":\"admin\",\"required\":false}]" + } + }, + { + "id": "3479dd56-02e9-4222-94fe-6a13cd065195", + "name": "User Resource Permission", + "type": "resource", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "resources": "[\"User Resource\"]", + "applyPolicies": "[\"Any User Policy\"]" + } + }, + { + "id": "60188298-d55b-4066-b231-6a7c56ff7cc5", + "name": "Administration Resource Permission", + "type": "resource", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "resources": "[\"Administration Resource\"]", + "applyPolicies": "[\"Only Administrators\"]" + } + } + ], + "scopes": [], + "decisionStrategy": "UNANIMOUS" + } + }, { + "id" : "376bd940-e50a-4495-80fc-9c6c07312748", + "clientId" : "realm-management", + "name" : "${client_realm-management}", + "surrogateAuthRequired" : false, + "enabled" : true, + "clientAuthenticatorType" : "client-secret", + "secret" : "c41b709a-a012-4c69-89d7-4f926dba0619", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : true, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "role_list", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "a8732cac-ae0f-44ec-b7f3-bd2c41eff13c", + "clientId" : "security-admin-console", + "name" : "${client_security-admin-console}", + "baseUrl" : "/auth/admin/quarkus/console/index.html", + "surrogateAuthRequired" : false, + "enabled" : true, + "clientAuthenticatorType" : "client-secret", + "secret" : "e571b211-2550-475d-b87f-116ff54091ee", + "redirectUris" : [ "/auth/admin/quarkus/console/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "280528ca-5e96-4bb9-9fc0-20311caac32d", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "web-origins", "role_list", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + } ], + "clientScopes" : [ { + "id" : "520cc3ef-2c6b-4d84-bcde-8c063241f4bd", + "name" : "address", + "description" : "OpenID Connect built-in scope: address", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${addressScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "c1d3bd07-0a5f-4f4f-b381-c58a7b723029", + "name" : "address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-address-mapper", + "consentRequired" : false, + "config" : { + "user.attribute.formatted" : "formatted", + "user.attribute.country" : "country", + "user.attribute.postal_code" : "postal_code", + "userinfo.token.claim" : "true", + "user.attribute.street" : "street", + "id.token.claim" : "true", + "user.attribute.region" : "region", + "access.token.claim" : "true", + "user.attribute.locality" : "locality" + } + } ] + }, { + "id" : "19920c96-a383-4f35-8ee9-27833263cf03", + "name" : "email", + "description" : "OpenID Connect built-in scope: email", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${emailScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "36a0adf0-6c25-419f-98d7-cdeada8661aa", + "name" : "email", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "email", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email", + "jsonType.label" : "String" + } + }, { + "id" : "b0c39901-5e5d-4436-b685-908bb90ea1d9", + "name" : "email verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "emailVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email_verified", + "jsonType.label" : "boolean" + } + } ] + }, { + "id" : "55b3ee1c-cbf9-4526-93d7-aa56a9c5f1cb", + "name" : "microprofile-jwt", + "description" : "Microprofile - JWT built-in scope", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "59128144-a21a-4744-bb55-e66ff0503b18", + "name" : "upn", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "upn", + "jsonType.label" : "String" + } + }, { + "id" : "69351a63-7d6e-45d0-be47-088c83b20fdb", + "name" : "groups", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "multivalued" : "true", + "user.attribute" : "foo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "groups", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "3f190f54-8e3a-4c82-a799-bd12ddc475b2", + "name" : "offline_access", + "description" : "OpenID Connect built-in scope: offline_access", + "protocol" : "openid-connect", + "attributes" : { + "consent.screen.text" : "${offlineAccessScopeConsentText}", + "display.on.consent.screen" : "true" + } + }, { + "id" : "defa3480-5368-4f34-8075-49fb982b71b3", + "name" : "phone", + "description" : "OpenID Connect built-in scope: phone", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${phoneScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "069ae414-9e98-4612-a3d6-e8b5a1fa841d", + "name" : "phone number verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumberVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number_verified", + "jsonType.label" : "boolean" + } + }, { + "id" : "cea58e24-d0e0-4cc6-9e34-7b3bf7d6d85b", + "name" : "phone number", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumber", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "b7321e2e-dd8e-41cf-a527-c765155c3f78", + "name" : "profile", + "description" : "OpenID Connect built-in scope: profile", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${profileScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "1d4d3df5-7af5-488e-8477-0ad7cb74d50a", + "name" : "nickname", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "nickname", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "nickname", + "jsonType.label" : "String" + } + }, { + "id" : "1a5e26d6-211e-4f8a-b696-0ea9577db25a", + "name" : "zoneinfo", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "zoneinfo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "zoneinfo", + "jsonType.label" : "String" + } + }, { + "id" : "18971685-6dd7-420f-9c09-879c4f2d54d8", + "name" : "updated at", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "updatedAt", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "updated_at", + "jsonType.label" : "String" + } + }, { + "id" : "b970d96b-0156-4db0-9beb-9c84c173e619", + "name" : "birthdate", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "birthdate", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "birthdate", + "jsonType.label" : "String" + } + }, { + "id" : "50287033-df21-45c6-aa46-c3060e6f9855", + "name" : "given name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "firstName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "given_name", + "jsonType.label" : "String" + } + }, { + "id" : "3dc6b97e-7063-4077-98d1-0cacf9029c7b", + "name" : "full name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-full-name-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "access.token.claim" : "true", + "userinfo.token.claim" : "true" + } + }, { + "id" : "3fb9391b-376c-42ef-b012-4df461c617cc", + "name" : "middle name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "middleName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "middle_name", + "jsonType.label" : "String" + } + }, { + "id" : "83f7fc4a-5386-4f86-a103-6585e138b61d", + "name" : "username", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "preferred_username", + "jsonType.label" : "String" + } + }, { + "id" : "8ef177b3-f485-44b1-afee-1901393b00c7", + "name" : "family name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "lastName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "family_name", + "jsonType.label" : "String" + } + }, { + "id" : "e994cbc7-2a1a-4465-b7b7-12b35b4fe49e", + "name" : "gender", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "gender", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "gender", + "jsonType.label" : "String" + } + }, { + "id" : "abaa4c9e-1fa2-4b45-a1bb-b3d650de9aca", + "name" : "picture", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "picture", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "picture", + "jsonType.label" : "String" + } + }, { + "id" : "bf21b514-81fd-4bbe-9236-bab5fcf54561", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + }, { + "id" : "254f8de4-08e7-4d3d-a87f-4b238f0f922b", + "name" : "profile", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "profile", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "profile", + "jsonType.label" : "String" + } + }, { + "id" : "7934bf2a-cfc3-4b2d-a5cb-287f3ed2a977", + "name" : "website", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "website", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "website", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "f3dc793d-6011-4861-b538-399dde5434c0", + "name" : "role_list", + "description" : "SAML role list", + "protocol" : "saml", + "attributes" : { + "consent.screen.text" : "${samlRoleListScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "22eeabf8-a3c3-4026-a351-367f8ace7927", + "name" : "role list", + "protocol" : "saml", + "protocolMapper" : "saml-role-list-mapper", + "consentRequired" : false, + "config" : { + "single" : "false", + "attribute.nameformat" : "Basic", + "attribute.name" : "Role" + } + } ] + }, { + "id" : "f72c1acd-c367-41b1-8646-b6bd5fff3e3f", + "name" : "roles", + "description" : "OpenID Connect scope for add user roles to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${rolesScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "cd8e589e-5fa7-4dae-bf6e-e8f6a3fd3cff", + "name" : "realm roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "realm_access.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + }, { + "id" : "708b19d1-0709-4278-b5a1-bcbeec11f51a", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { } + }, { + "id" : "25e97210-30c7-4f35-be11-407f1fa674cb", + "name" : "client roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-client-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "resource_access.${client_id}.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + } ] + }, { + "id" : "52618957-a4e8-4c6f-a902-217f2c41a2fd", + "name" : "web-origins", + "description" : "OpenID Connect scope for add allowed web origins to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false", + "consent.screen.text" : "" + }, + "protocolMappers" : [ { + "id" : "a66ddadf-312f-491f-993c-fa58685815c6", + "name" : "allowed web origins", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-allowed-origins-mapper", + "consentRequired" : false, + "config" : { } + } ] + } ], + "defaultDefaultClientScopes" : [ "role_list", "profile", "email", "roles", "web-origins" ], + "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt" ], + "browserSecurityHeaders" : { + "contentSecurityPolicyReportOnly" : "", + "xContentTypeOptions" : "nosniff", + "xRobotsTag" : "none", + "xFrameOptions" : "SAMEORIGIN", + "xXSSProtection" : "1; mode=block", + "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "strictTransportSecurity" : "max-age=31536000; includeSubDomains" + }, + "smtpServer" : { }, + "eventsEnabled" : false, + "eventsListeners" : [ "jboss-logging" ], + "enabledEventTypes" : [ ], + "adminEventsEnabled" : false, + "adminEventsDetailsEnabled" : false, + "components" : { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ { + "id" : "a7679218-373d-48ca-88f8-429985faeae3", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "oidc-full-name-mapper", "saml-user-attribute-mapper", "saml-user-property-mapper", "oidc-address-mapper", "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper" ] + } + }, { + "id" : "2ebf6f9f-4bfc-44b9-ad7c-282f2274d35b", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "552093c3-0a0a-4234-ad7c-ae660f0f0db1", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "8f27cf74-cee7-4a73-851f-982ee45157ca", + "name" : "Trusted Hosts", + "providerId" : "trusted-hosts", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "host-sending-registration-request-must-match" : [ "true" ], + "client-uris-must-match" : [ "true" ] + } + }, { + "id" : "ff570525-6c96-4500-9d73-c02e708b39de", + "name" : "Full Scope Disabled", + "providerId" : "scope", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "b52284eb-123a-4718-aac9-857530a24a9b", + "name" : "Max Clients Limit", + "providerId" : "max-clients", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "max-clients" : [ "200" ] + } + }, { + "id" : "2b8c0a6d-d5c0-4ea2-8a9c-4843d3e04ec6", + "name" : "Consent Required", + "providerId" : "consent-required", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "bf59de5a-2c93-43cc-a9aa-03be0129fe53", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "saml-user-property-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper", "oidc-usermodel-property-mapper", "oidc-sha256-pairwise-sub-mapper" ] + } + } ], + "org.keycloak.keys.KeyProvider" : [ { + "id" : "b3efd9cc-28b6-4404-82af-8a48a966b8ff", + "name" : "rsa-generated", + "providerId" : "rsa-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEowIBAAKCAQEAn5T13suF8mlS+pJXp0U1bto41nW55wpcs+Rps8ZVCRyJKWqzwSCYnI7lm0rB2wBpAAO4OPoj1zlmVoFmBPsDU9Xf7rjsJb5LIzIQDCZY44aSDZt6RR+gakPiQvlzHyW/RozYpngDJF7TsTD7rdRF1xQ4RprfBF8fwK/xsU7pxbeom5xDHZhz3fiw8s+7UdbmnazDHfAjU58aUrLGgVRfUsuoHjtsptYlOIXEifaeMetXZE+HhqLYRHQPDap5fbBJl773Trosn7N9nmzN4x1xxGj9So21WC5UboQs9sAIVgizc4omjZ5Y4RN9HLH7G4YwJctNntzmnJhDui9zAO+zSQIDAQABAoIBADi+F7rTtVoft0Cfnok8o6Y58/HVxHdxiMryUd95iy0FN4RBi48FTx6D9QKFz25Ws/8sU2n3D51srIXf1u24b1N0/f39RQKaqk7mcyxOylaEuBQcj5pah4ihgKd92UBfBKdKV5LBo6RgD3e2yhbiHr8+UlBQqzH7vOef6Bm6zIbfmi3N88swAJhP0YizRZFklsbmLsK6nkwyro00CHJvPVKSBbM+ad+/zIBsLw56MvNngB5TuFguUgoljd6M1T2z4utmZGlTUqrfE1onAVLJZoGnRohyIr7dJEg6YxWR70PxsgmkDKyeRvet9P1trO0n+OSprusfrC3cHJStabap1V0CgYEA1A/CtsqTnjdYYsB19eumZgdpzUgNc/YEAzZ/OWb8yTLoB2ncci+63A1rXHUXAqJFY7vtjn5mxv7SuASNbUrzq+6KfZvC1x9XEtnczqT/ypunNfxmIZuj8Nuu6vtURguZ8kPPwdkI8toTizRFeRE5ZDBvoQryiEVYugfHaHT5vzsCgYEAwKWODwquI0Lv9BuwdNVrBXQpkKh3ZfYOA7i9xvhxlM7xUu8OMCwwCPn3r7vrW5APjTqX4h330mJ44SLEs+7gbCUs4BbJBLA6g0ChlHa9PTkxp6tk2nDF/B34fxiZSRkE85L+d+at0Dc3hnlzLCJCzJawGpoPniPU9e4w0p4dN0sCgYAsGnMGjS8SUrRhJWHjGXVr9tK8TOXvXhULjgP7rj2Yoqu7Dvs4DFEyft/7RKbad2EzEtyfLA64CDtO5jN7rYDsGxpWcVSeZPg5BXJ0z8AbJTArfCjJiJMZ/rZsTIUEZFlKF2xYBolj6JLz+pUQTtK+0YwF1D8ItFN1rTR9twZSDQKBgQC6sPXNX+VH6LuPTjIf1x8CxwLs3EXxOpV0R9kp9GRl+HJnk6GlT30xhcThufQo5KAdllXQXIhoiuNoEoCbevhj9Vbax1oBQCNERSMRNEzKAx46xd9TzYwgeo7x5E3QR/3DaoVOfu+cY5ZcrF/PulgP2kxJS1mtQD5GIpGP2oinpwKBgGqiqTFPqRcelx76vBvTU+Jp1zM62T4AotbMrSQR/oUvqHe5Ytj/SbZx+wbbHAiyGgV700Mosyviik83YEAbR3kdOPjgYvAJJW2Y3jEMdQ7MwriXz8XLh5BGmYfVjkSOJXed9ua9WlYLKOJeXXv191BbDvrx5NXuJyVVU4vJx3YZ" ], + "certificate" : [ "MIICnTCCAYUCBgFp4EYIrjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdwcm90ZWFuMB4XDTE5MDQwMjIyNTYxOVoXDTI5MDQwMjIyNTc1OVowEjEQMA4GA1UEAwwHcHJvdGVhbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ+U9d7LhfJpUvqSV6dFNW7aONZ1uecKXLPkabPGVQkciSlqs8EgmJyO5ZtKwdsAaQADuDj6I9c5ZlaBZgT7A1PV3+647CW+SyMyEAwmWOOGkg2bekUfoGpD4kL5cx8lv0aM2KZ4AyRe07Ew+63URdcUOEaa3wRfH8Cv8bFO6cW3qJucQx2Yc934sPLPu1HW5p2swx3wI1OfGlKyxoFUX1LLqB47bKbWJTiFxIn2njHrV2RPh4ai2ER0Dw2qeX2wSZe+9066LJ+zfZ5szeMdccRo/UqNtVguVG6ELPbACFYIs3OKJo2eWOETfRyx+xuGMCXLTZ7c5pyYQ7ovcwDvs0kCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAVtmRKDb4OK5iSA46tagMBkp6L7WuPpCWuHGWwobEP+BecYsShW7zP3s12oA8SNSwbhvu0CRqgzxhuypgf3hKQFVU153Erv4hzkj+8S0s5LR/ZE7tDNY2lzJ3yQKXy3Md7EkuzzvOZ50MTrcSKAanWq/ZW1OTnrtGymj5zGJnTg7mMnJzEIGePxkvPu/QdchiPBLqxfZYm1jsFGY25djOC3N/KmVcRVmPRGuu6D8tBFHlKoPfZYPdbMvsvs24aupHKRcZ+ofTCpK+2Qo8c0pSSqeEYHGmuGqC6lC6ozxtxSABPO9Q1R1tZBU7Kg5HvXUwwmoVS3EGub46YbHqbmWMLg==" ], + "priority" : [ "100" ] + } + }, { + "id" : "20460ca5-ec24-4a9b-839a-457743d3f841", + "name" : "hmac-generated", + "providerId" : "hmac-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "96afd00e-85cf-4d35-b18e-061d3813d8b2" ], + "secret" : [ "qBFGKdUGf6xDgKphnRfoFzIzaFHJW4bYnZ9MinPFzN38X5_ctq-2u1q5RdZzeJukXvk2biHB8_s3DxWmmLZFsA" ], + "priority" : [ "100" ], + "algorithm" : [ "HS256" ] + } + }, { + "id" : "4f02d984-7a23-4ce1-8591-848a71390efe", + "name" : "aes-generated", + "providerId" : "aes-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "b04473d3-8395-4016-b455-19a9e951106b" ], + "secret" : [ "x68mMOVdz3qKWzltzReV0g" ], + "priority" : [ "100" ] + } + } ] + }, + "internationalizationEnabled" : false, + "supportedLocales" : [ ], + "authenticationFlows" : [ { + "id" : "d6c3e282-a738-4b8b-98c2-378b9faf8344", + "alias" : "Handle Existing Account", + "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-confirm-link", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "idp-email-verification", + "requirement" : "ALTERNATIVE", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "requirement" : "ALTERNATIVE", + "priority" : 30, + "flowAlias" : "Verify Existing Account by Re-authentication", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "4855860b-4009-4f1b-ba6b-60581618ea62", + "alias" : "Verify Existing Account by Re-authentication", + "description" : "Reauthentication of existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-username-password-form", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "auth-otp-form", + "requirement" : "OPTIONAL", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "8a9872b0-65f1-47ff-9565-fa826ac64cd4", + "alias" : "browser", + "description" : "browser based authentication", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-cookie", + "requirement" : "ALTERNATIVE", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "auth-spnego", + "requirement" : "DISABLED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "identity-provider-redirector", + "requirement" : "ALTERNATIVE", + "priority" : 25, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "requirement" : "ALTERNATIVE", + "priority" : 30, + "flowAlias" : "forms", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "51b8ed14-62b6-49b3-b602-0b51508349e0", + "alias" : "clients", + "description" : "Base authentication for clients", + "providerId" : "client-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "client-secret", + "requirement" : "ALTERNATIVE", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "client-jwt", + "requirement" : "ALTERNATIVE", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "client-secret-jwt", + "requirement" : "ALTERNATIVE", + "priority" : 30, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "client-x509", + "requirement" : "ALTERNATIVE", + "priority" : 40, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "9b65133a-ee71-494a-a659-6804513fc30b", + "alias" : "direct grant", + "description" : "OpenID Connect Resource Owner Grant", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "direct-grant-validate-username", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "direct-grant-validate-password", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "direct-grant-validate-otp", + "requirement" : "OPTIONAL", + "priority" : 30, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "f62bc4ad-25ac-4f83-963b-32820af3a683", + "alias" : "docker auth", + "description" : "Used by Docker clients to authenticate against the IDP", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "docker-http-basic-authenticator", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "1b423fe7-f312-404c-903b-f1260a77259b", + "alias" : "first broker login", + "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "review profile config", + "authenticator" : "idp-review-profile", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticatorConfig" : "create unique user config", + "authenticator" : "idp-create-user-if-unique", + "requirement" : "ALTERNATIVE", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "requirement" : "ALTERNATIVE", + "priority" : 30, + "flowAlias" : "Handle Existing Account", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "9c9530b3-e3c6-481b-99e8-1461a9752e8e", + "alias" : "forms", + "description" : "Username, password, otp and other auth forms.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-username-password-form", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "auth-otp-form", + "requirement" : "OPTIONAL", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "70fb94ac-354c-4629-a5fe-5135d0137964", + "alias" : "http challenge", + "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "no-cookie-redirect", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "basic-auth", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "basic-auth-otp", + "requirement" : "DISABLED", + "priority" : 30, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "auth-spnego", + "requirement" : "DISABLED", + "priority" : 40, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "08292a4a-6722-4e33-a5d9-354c2628f567", + "alias" : "registration", + "description" : "registration flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-page-form", + "requirement" : "REQUIRED", + "priority" : 10, + "flowAlias" : "registration form", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "668dc4b6-fe1a-4d24-ab5b-bc76e20ac390", + "alias" : "registration form", + "description" : "registration form", + "providerId" : "form-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-user-creation", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "registration-profile-action", + "requirement" : "REQUIRED", + "priority" : 40, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "registration-password-action", + "requirement" : "REQUIRED", + "priority" : 50, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "registration-recaptcha-action", + "requirement" : "DISABLED", + "priority" : 60, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "a0e191f0-ce9a-4a75-b6e4-97332b05f7e5", + "alias" : "reset credentials", + "description" : "Reset credentials for a user if they forgot their password or something", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "reset-credentials-choose-user", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "reset-credential-email", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "reset-password", + "requirement" : "REQUIRED", + "priority" : 30, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "reset-otp", + "requirement" : "OPTIONAL", + "priority" : 40, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "ad4beb21-8e9a-4fca-af41-0f757169f26c", + "alias" : "saml ecp", + "description" : "SAML ECP Profile Authentication Flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "http-basic-authenticator", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + } ], + "authenticatorConfig" : [ { + "id" : "25632f91-6071-423a-8e9c-7322cdc1b011", + "alias" : "create unique user config", + "config" : { + "require.password.update.after.registration" : "false" + } + }, { + "id" : "02d7f70b-1ebc-4e72-a65c-d94a600895ac", + "alias" : "review profile config", + "config" : { + "update.profile.on.first.login" : "missing" + } + } ], + "requiredActions" : [ { + "alias" : "CONFIGURE_TOTP", + "name" : "Configure OTP", + "providerId" : "CONFIGURE_TOTP", + "enabled" : true, + "defaultAction" : false, + "priority" : 10, + "config" : { } + }, { + "alias" : "terms_and_conditions", + "name" : "Terms and Conditions", + "providerId" : "terms_and_conditions", + "enabled" : false, + "defaultAction" : false, + "priority" : 20, + "config" : { } + }, { + "alias" : "UPDATE_PASSWORD", + "name" : "Update Password", + "providerId" : "UPDATE_PASSWORD", + "enabled" : true, + "defaultAction" : false, + "priority" : 30, + "config" : { } + }, { + "alias" : "UPDATE_PROFILE", + "name" : "Update Profile", + "providerId" : "UPDATE_PROFILE", + "enabled" : true, + "defaultAction" : false, + "priority" : 40, + "config" : { } + }, { + "alias" : "VERIFY_EMAIL", + "name" : "Verify Email", + "providerId" : "VERIFY_EMAIL", + "enabled" : true, + "defaultAction" : false, + "priority" : 50, + "config" : { } + } ], + "browserFlow" : "browser", + "registrationFlow" : "registration", + "directGrantFlow" : "direct grant", + "resetCredentialsFlow" : "reset credentials", + "clientAuthenticationFlow" : "clients", + "dockerAuthenticationFlow" : "docker auth", + "attributes" : { + "_browser_header.xXSSProtection" : "1; mode=block", + "_browser_header.xFrameOptions" : "SAMEORIGIN", + "_browser_header.strictTransportSecurity" : "max-age=31536000; includeSubDomains", + "permanentLockout" : "false", + "quickLoginCheckMilliSeconds" : "1000", + "_browser_header.xRobotsTag" : "none", + "maxFailureWaitSeconds" : "900", + "minimumQuickLoginWaitSeconds" : "60", + "failureFactor" : "30", + "actionTokenGeneratedByUserLifespan" : "300", + "maxDeltaTimeSeconds" : "43200", + "_browser_header.xContentTypeOptions" : "nosniff", + "offlineSessionMaxLifespan" : "5184000", + "actionTokenGeneratedByAdminLifespan" : "43200", + "_browser_header.contentSecurityPolicyReportOnly" : "", + "bruteForceProtected" : "false", + "_browser_header.contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "waitIncrementSeconds" : "60", + "offlineSessionMaxLifespanEnabled" : "false" + }, + "users" : [ { + "id" : "af134cab-f41c-4675-b141-205f975db679", + "username" : "admin", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "credentials" : [ { + "type" : "password", + "hashedSaltedValue" : "NICTtwsvSxJ5hL8hLAuleDUv9jwZcuXgxviMXvR++cciyPtiIEStEaJUyfA9DOir59awjPrHOumsclPVjNBplA==", + "salt" : "T/2P5o5oxFJUEk68BRURRg==", + "hashIterations" : 27500, + "counter" : 0, + "algorithm" : "pbkdf2-sha256", + "digits" : 0, + "period" : 0, + "createdDate" : 1554245879354, + "config" : { } + } ], + "disableableCredentialTypes" : [ "password" ], + "requiredActions" : [ ], + "realmRoles" : [ "admin", "user" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "eb4123a3-b722-4798-9af5-8957f823657a", + "username" : "alice", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "credentials" : [ { + "type" : "password", + "hashedSaltedValue" : "A3okqV2T/ybXTVEgKfosoSjP8Yc9IZbFP/SY4cEd6hag7TABQrQ6nUSuwagGt96l8cw1DTijO75PqX6uiTXMzw==", + "salt" : "sl4mXx6T9FypPH/s9TngfQ==", + "hashIterations" : 27500, + "counter" : 0, + "algorithm" : "pbkdf2-sha256", + "digits" : 0, + "period" : 0, + "createdDate" : 1554245879116, + "config" : { } + } ], + "disableableCredentialTypes" : [ "password" ], + "requiredActions" : [ ], + "realmRoles" : [ "user" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "1eed6a8e-a853-4597-b4c6-c4c2533546a0", + "username" : "jdoe", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "credentials" : [ { + "type" : "password", + "hashedSaltedValue" : "JV3DUNLjqOadjbBOtC4rvacQI553CGaDGAzBS8MR5ReCr7SwF3E6CsW3T7/XO8ITZAsch8+A/6loeuCoVLLJrg==", + "salt" : "uCbOH7HZtyDtMd0E9DG/nw==", + "hashIterations" : 27500, + "counter" : 0, + "algorithm" : "pbkdf2-sha256", + "digits" : 0, + "period" : 0, + "createdDate" : 1554245879227, + "config" : { } + } ], + "disableableCredentialTypes" : [ "password" ], + "requiredActions" : [ ], + "realmRoles" : [ "confidential", "user" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "948c59ec-46ed-4d99-aa43-02900029b930", + "createdTimestamp" : 1554245880023, + "username" : "service-account-backend-service", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "service-account-backend-service@placeholder.org", + "serviceAccountClientId" : "backend-service", + "credentials" : [ ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "offline_access" ], + "clientRoles" : { + "backend-service" : [ "uma_protection" ], + "account" : [ "view-profile", "manage-account" ] + }, + "notBefore" : 0, + "groups" : [ ] + } ], + "keycloakVersion" : "6.0.0", + "userManagedAccessAllowed" : false +} diff --git a/integration/quarkus-keycloak-authorization/docker-compose.yaml b/integration/quarkus-keycloak-authorization/docker-compose.yaml new file mode 100644 index 0000000..d80cf0e --- /dev/null +++ b/integration/quarkus-keycloak-authorization/docker-compose.yaml @@ -0,0 +1,18 @@ +version: "3" + +services: + postgresdb: + image: docker.io/bitnami/postgresql:14 + container_name: postgresdb + environment: + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_USER: demo + POSTGRES_PASSWORD: demo + POSTGRES_DB: demo + ports: + - "5432:5432" + networks: + - demo + +networks: + demo: \ No newline at end of file diff --git a/integration/quarkus-keycloak-authorization/mvnw b/integration/quarkus-keycloak-authorization/mvnw new file mode 100644 index 0000000..5643201 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/integration/quarkus-keycloak-authorization/mvnw.cmd b/integration/quarkus-keycloak-authorization/mvnw.cmd new file mode 100644 index 0000000..8a15b7f --- /dev/null +++ b/integration/quarkus-keycloak-authorization/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/integration/quarkus-keycloak-authorization/pom.xml b/integration/quarkus-keycloak-authorization/pom.xml new file mode 100644 index 0000000..d4e0958 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/pom.xml @@ -0,0 +1,312 @@ + + + 4.0.0 + + com.devonfw.quarkus + quarkus-keycloak-authorization + 1.0.0-SNAPSHOT + + + quarkus-bom + io.quarkus + 2.12.2.Final +2.12.2.Final +3.10.1 + 3.0.0-M7 + 11 + 11 + UTF-8 + 1.4.2.Final + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + + + + io.quarkus + quarkus-smallrye-jwt + + + io.quarkus + quarkus-smallrye-jwt-build + + + + + io.quarkus + quarkus-keycloak-authorization + + + io.quarkus + quarkus-oidc + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + + + io.quarkus + quarkus-smallrye-openapi + + + + + + + io.quarkus + quarkus-jdbc-postgresql + + + + org.hibernate + hibernate-jpamodelgen + + + io.quarkus + quarkus-spring-data-jpa + + + + + io.quarkus + quarkus-flyway + + + + com.querydsl + querydsl-jpa + 4.3.1 + + + com.querydsl + querydsl-apt + provided + 4.3.1 + + + + + io.quarkus + quarkus-logging-json + + + + + org.projectlombok + lombok + 1.18.18 + provided + + + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + provided + + + org.mapstruct + mapstruct + ${mapstruct.version} + provided + + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + provided + + + + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-test-keycloak-server + test + + + + + + + src/main/resources + + + config + + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus-plugin.version} + true + + + + build + generate-code + generate-code-tests + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + true + + + -Amapstruct.defaultComponentModel=cdi + -Amapstruct.defaultInjectionStrategy=CONSTRUCTOR + + + + + + maven-surefire-plugin + ${surefire.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + + + + build + + + + + + + com.mysema.maven + apt-maven-plugin + 1.1.3 + + + generate-sources + + process + + + target/generated-sources/annotations + com.querydsl.apt.jpa.JPAAnnotationProcessor + + + + + + + + + + + native + + + native + + + + native + true + + + + + maven-failsafe-plugin + ${surefire.version} + + + + integration-test + verify + + + + + ${project.build.directory}/${project.build.finalName}-runner + + + org.jboss.logmanager.LogManager + + ${maven.home} + + + + + + + + + + docker + + + start-containers + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + + docker-prune + generate-resources + + exec + + + ${basedir}/../.github/docker-prune.sh + + + + + + + + + diff --git a/integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.jvm b/integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.jvm new file mode 100644 index 0000000..1d54df5 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.jvm @@ -0,0 +1,54 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/security-keycloak-authorization-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/security-keycloak-authorization-jvm +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050 +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/security-keycloak-authorization-jvm +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3 + +ARG JAVA_PACKAGE=java-11-openjdk-headless +ARG RUN_JAVA_VERSION=1.3.8 +ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' +# Install java and the run-java script +# Also set up permissions for user `1001` +RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ + && microdnf update \ + && microdnf clean all \ + && mkdir /deployments \ + && chown 1001 /deployments \ + && chmod "g+rwX" /deployments \ + && chown 1001:root /deployments \ + && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ + && chown 1001 /deployments/run-java.sh \ + && chmod 540 /deployments/run-java.sh \ + && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security + +# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. +ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --chown=1001 target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=1001 target/quarkus-app/*.jar /deployments/ +COPY --chown=1001 target/quarkus-app/app/ /deployments/app/ +COPY --chown=1001 target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT [ "/deployments/run-java.sh" ] diff --git a/integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.legacy-jar b/integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.legacy-jar new file mode 100644 index 0000000..fdb0863 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.legacy-jar @@ -0,0 +1,51 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package -Dquarkus.package.type=legacy-jar +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/security-keycloak-authorization-legacy-jar . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/security-keycloak-authorization-legacy-jar +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050 +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/security-keycloak-authorization-legacy-jar +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3 + +ARG JAVA_PACKAGE=java-11-openjdk-headless +ARG RUN_JAVA_VERSION=1.3.8 +ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' +# Install java and the run-java script +# Also set up permissions for user `1001` +RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ + && microdnf update \ + && microdnf clean all \ + && mkdir /deployments \ + && chown 1001 /deployments \ + && chmod "g+rwX" /deployments \ + && chown 1001:root /deployments \ + && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ + && chown 1001 /deployments/run-java.sh \ + && chmod 540 /deployments/run-java.sh \ + && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security + +# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. +ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +COPY target/lib/* /deployments/lib/ +COPY target/*-runner.jar /deployments/app.jar + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT [ "/deployments/run-java.sh" ] diff --git a/integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.native b/integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.native new file mode 100644 index 0000000..aa62636 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.native @@ -0,0 +1,27 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode +# +# Before building the container image run: +# +# ./mvnw package -Pnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t quarkus/security-keycloak-authorization . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/security-keycloak-authorization +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.native-micro b/integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.native-micro new file mode 100644 index 0000000..663b8d2 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/docker/Dockerfile.native-micro @@ -0,0 +1,23 @@ +#### +# This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode +# +# Before building the container image run: +# +# ./mvnw package -Pnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/security-keycloak-authorization . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/security-keycloak-authorization +# +### +FROM quay.io/quarkus/quarkus-micro-image:1.0 +COPY target/*-runner /application + +EXPOSE 8080 +USER 1001 + +CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/DemoApplication.java b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/DemoApplication.java new file mode 100644 index 0000000..3acb3d1 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/DemoApplication.java @@ -0,0 +1,17 @@ +package com.devonfw.quarkus; + +import javax.ws.rs.core.Application; + +import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition; +import org.eclipse.microprofile.openapi.annotations.info.Contact; +import org.eclipse.microprofile.openapi.annotations.info.Info; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; + +@OpenAPIDefinition(tags = { +@Tag(name = "product", description = "Product API.") }, info = @Info(title = "Quarkus Keycloak AuthN AuthZ demo", version = "1.0.0", contact = @Contact(name = "API Support", email = "support@devonfw.com"))) +/** + * JaxRS application is not required in quarkus, but it is useful to place central API docs somewhere. We could also use + * package-info.java for this. + */ +public class DemoApplication extends Application { +} \ No newline at end of file diff --git a/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/general/domain/model/ApplicationPersistenceEntity.java b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/general/domain/model/ApplicationPersistenceEntity.java new file mode 100644 index 0000000..51e7550 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/general/domain/model/ApplicationPersistenceEntity.java @@ -0,0 +1,79 @@ +package com.devonfw.quarkus.general.domain.model; + +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; +import javax.persistence.Transient; +import javax.persistence.Version; + +/** + * Abstract base class for all {@link PersistenceEntity persistence entities} with an {@link #getId() id} and a + * {@link #getModificationCounter() modificationCounter} (version) field. All persistence entities of this application + * should inherit from this class. It is using JPA annotations at the getters what has several advantages but also + * implies that you have to annotate transient getter methods with the {@link Transient} annotation. + */ +@MappedSuperclass +public abstract class ApplicationPersistenceEntity { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Version + private Integer modificationCounter; + + /** + * The constructor. + */ + public ApplicationPersistenceEntity() { + + super(); + } + + public Long getId() { + + return this.id; + } + + public void setId(Long id) { + + this.id = id; + } + + public Integer getModificationCounter() { + + return this.modificationCounter; + } + + public void setModificationCounter(Integer version) { + + this.modificationCounter = version; + } + + @Override + public String toString() { + + StringBuilder buffer = new StringBuilder(); + toString(buffer); + return buffer.toString(); + } + + /** + * Method to extend {@link #toString()} logic. + * + * @param buffer is the {@link StringBuilder} where to {@link StringBuilder#append(Object) append} the string + * representation. + */ + protected void toString(StringBuilder buffer) { + + buffer.append(getClass().getSimpleName()); + if (this.id != null) { + buffer.append("[id="); + buffer.append(this.id); + buffer.append("]"); + } + } +} diff --git a/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/general/domain/model/ApplicationSearchCriteriaDto.java b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/general/domain/model/ApplicationSearchCriteriaDto.java new file mode 100644 index 0000000..160fa3d --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/general/domain/model/ApplicationSearchCriteriaDto.java @@ -0,0 +1,23 @@ +package com.devonfw.quarkus.general.domain.model; + +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +public class ApplicationSearchCriteriaDto { + + @Schema(description = "Page Number", defaultValue = "0") + private int pageNumber = 0; + + @Schema(description = "Page Size", defaultValue = "10") + private int pageSize = 10; + + @Schema(description = "determine total") + private boolean determineTotal = false; + +} diff --git a/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/domain/model/ProductEntity.java b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/domain/model/ProductEntity.java new file mode 100644 index 0000000..b7b22ac --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/domain/model/ProductEntity.java @@ -0,0 +1,26 @@ +package com.devonfw.quarkus.productmanagement.domain.model; + +import java.math.BigDecimal; + +import javax.persistence.Entity; +import javax.persistence.Table; + +import com.devonfw.quarkus.general.domain.model.ApplicationPersistenceEntity; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "Product") +// A JPA entity requires at least 2 things @Entity annotation and an ID +// by default, the DB table will have the same name as our class +public class ProductEntity extends ApplicationPersistenceEntity { + + private String title; + + private String description; + + private BigDecimal price; +} diff --git a/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/domain/repo/ProductRepository.java b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/domain/repo/ProductRepository.java new file mode 100644 index 0000000..32d312d --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/domain/repo/ProductRepository.java @@ -0,0 +1,16 @@ +package com.devonfw.quarkus.productmanagement.domain.repo; + +import org.springframework.data.domain.Page; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; + +import com.devonfw.quarkus.productmanagement.domain.model.ProductEntity; + +public interface ProductRepository extends CrudRepository { + + @Query("select a from ProductEntity a where title = :title") + ProductEntity findByTitle(@Param("title") String title); + + Page findAllByOrderByTitle(); +} \ No newline at end of file diff --git a/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/ProductReadService.java b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/ProductReadService.java new file mode 100644 index 0000000..2c3ff79 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/ProductReadService.java @@ -0,0 +1,85 @@ +package com.devonfw.quarkus.productmanagement.rest.v1; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import javax.inject.Inject; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.eclipse.microprofile.jwt.JsonWebToken; +import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeType; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; +import org.eclipse.microprofile.openapi.annotations.security.SecurityScheme; +import org.jose4j.json.internal.json_simple.JSONObject; +import org.springframework.data.domain.Page; + +import com.devonfw.quarkus.productmanagement.domain.model.ProductEntity; +import com.devonfw.quarkus.productmanagement.domain.repo.ProductRepository; +import com.devonfw.quarkus.productmanagement.rest.v1.mapper.ProductMapper; +import com.devonfw.quarkus.productmanagement.rest.v1.model.ProductDto; + +@Path("/product/v1/users") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@SecurityScheme(securitySchemeName = "jwt", type = SecuritySchemeType.HTTP, scheme = "bearer", bearerFormat = "jwt") +public class ProductReadService { + + @Inject + JsonWebToken jwt; + + @Inject + ProductRepository productRepository; + + @Inject + ProductMapper productMapper; + + @GET + @Path("claims") + @SecurityRequirement(name = "jwt", scopes = {}) + public String getClaims() { + + Map claims = new HashMap<>(); + this.jwt.getClaimNames().stream() + .forEach(claimName -> claims.put(claimName, this.jwt.getClaim(claimName).toString())); + return new JSONObject(claims).toJSONString(); + } + + @GET + @SecurityRequirement(name = "jwt", scopes = {}) + public Page getProducts() { + + Page products = this.productRepository.findAllByOrderByTitle(); + if (products != null) { + return this.productMapper.map(products); + } + return null; + } + + @GET + @Path("{id}") + @SecurityRequirement(name = "jwt", scopes = {}) + public ProductDto getProductById(@Parameter(description = "Product unique id") @PathParam("id") String id) { + + Optional product = this.productRepository.findById(Long.valueOf(id)); + if (product.isPresent()) { + return this.productMapper.map(product.get()); + } + return null; + } + + @GET + @Path("title/{title}") + @SecurityRequirement(name = "jwt", scopes = {}) + public ProductDto getProductByTitle(@PathParam("title") String title) { + + return this.productMapper.map(this.productRepository.findByTitle(title)); + } + +} diff --git a/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/ProductWriteService.java b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/ProductWriteService.java new file mode 100644 index 0000000..bf6072d --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/ProductWriteService.java @@ -0,0 +1,81 @@ +package com.devonfw.quarkus.productmanagement.rest.v1; + +import static com.devonfw.quarkus.productmanagement.utils.StringUtils.isEmpty; +import static javax.ws.rs.core.Response.created; +import static javax.ws.rs.core.Response.status; + +import javax.inject.Inject; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; + +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; + +import com.devonfw.quarkus.productmanagement.domain.model.ProductEntity; +import com.devonfw.quarkus.productmanagement.domain.repo.ProductRepository; +import com.devonfw.quarkus.productmanagement.rest.v1.mapper.ProductMapper; +import com.devonfw.quarkus.productmanagement.rest.v1.model.ProductDto; +import com.devonfw.quarkus.productmanagement.rest.v1.model.User; + +import io.quarkus.security.identity.SecurityIdentity; + +@Path("/product/v1/admin") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class ProductWriteService { + + @Context + UriInfo uriInfo; + + @Inject + ProductRepository productRepository; + + @Inject + ProductMapper productMapper; + + @Inject + SecurityIdentity keycloakSecurityContext; + + @GET + @SecurityRequirement(name = "jwt", scopes = {}) + public User manage() { + + return new User(this.keycloakSecurityContext); + } + + @POST + @SecurityRequirement(name = "jwt", scopes = {}) + public Response createNewProduct(ProductDto product) { + + if (isEmpty(product.getTitle())) { + throw new WebApplicationException("Title was not set on request.", 400); + } + + ProductEntity productEntity = this.productRepository.save(this.productMapper.map(product)); + + UriBuilder uriBuilder = this.uriInfo.getAbsolutePathBuilder().path(Long.toString(productEntity.getId())); + return created(uriBuilder.build()).build(); + } + + @DELETE + @Path("{id}") + @SecurityRequirement(name = "jwt", scopes = {}) + public Response deleteProductById(@Parameter(description = "Product unique id") @PathParam("id") String id) { + + this.productRepository.deleteById(Long.valueOf(id)); + return status(Status.NO_CONTENT.getStatusCode()).build(); + } + +} diff --git a/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/mapper/ProductMapper.java b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/mapper/ProductMapper.java new file mode 100644 index 0000000..9c36b77 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/mapper/ProductMapper.java @@ -0,0 +1,26 @@ +package com.devonfw.quarkus.productmanagement.rest.v1.mapper; + +import java.util.List; + +import org.mapstruct.Mapper; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; + +import com.devonfw.quarkus.productmanagement.domain.model.ProductEntity; +import com.devonfw.quarkus.productmanagement.rest.v1.model.ProductDto; + +@Mapper +public interface ProductMapper { + + ProductDto map(ProductEntity model); + + ProductEntity map(ProductDto dto); + + List map(List Products); + + default Page map(Page products) { + + List productsDto = this.map(products.getContent()); + return new PageImpl<>(productsDto, products.getPageable(), products.getTotalElements()); + } +} diff --git a/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/AbstractDto.java b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/AbstractDto.java new file mode 100644 index 0000000..dd920a2 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/AbstractDto.java @@ -0,0 +1,14 @@ +package com.devonfw.quarkus.productmanagement.rest.v1.model; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +public abstract class AbstractDto { + private int modificationCounter; + + private Long id; +} diff --git a/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/ProductDto.java b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/ProductDto.java new file mode 100644 index 0000000..1f11e7c --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/ProductDto.java @@ -0,0 +1,29 @@ +package com.devonfw.quarkus.productmanagement.rest.v1.model; + +import java.math.BigDecimal; + +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ProductDto extends AbstractDto { + + @Schema(nullable = false, description = "Product title", minLength = 3, maxLength = 500) + private String title; + + @Schema(description = "Product description", minLength = 3, maxLength = 500) + private String description; + + @Schema(description = "Product price") + private BigDecimal price; + +} diff --git a/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/ProductSearchCriteriaDto.java b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/ProductSearchCriteriaDto.java new file mode 100644 index 0000000..7486174 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/ProductSearchCriteriaDto.java @@ -0,0 +1,25 @@ +package com.devonfw.quarkus.productmanagement.rest.v1.model; + +import java.math.BigDecimal; + +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import com.devonfw.quarkus.general.domain.model.ApplicationSearchCriteriaDto; + +import lombok.Data; + +@Data +public class ProductSearchCriteriaDto extends ApplicationSearchCriteriaDto { + + @Schema(description = "Product title") + private String title; + + @Schema(description = "Product Min price") + private BigDecimal priceMin; + + @Schema(description = "Product Max price") + private BigDecimal priceMax; + + @Schema(description = "Product price") + private BigDecimal price; +} diff --git a/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/User.java b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/User.java new file mode 100644 index 0000000..4e50315 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/rest/v1/model/User.java @@ -0,0 +1,18 @@ +package com.devonfw.quarkus.productmanagement.rest.v1.model; + +import io.quarkus.security.identity.SecurityIdentity; + +public class User { + + private final String userName; + + public User(SecurityIdentity securityContext) { + + this.userName = securityContext.getPrincipal().getName(); + } + + public String getUserName() { + + return this.userName; + } +} \ No newline at end of file diff --git a/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/utils/QueryUtil.java b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/utils/QueryUtil.java new file mode 100644 index 0000000..24ec6d0 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/utils/QueryUtil.java @@ -0,0 +1,32 @@ +package com.devonfw.quarkus.productmanagement.utils; + +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; + +import com.querydsl.jpa.impl.JPAQuery; + +public class QueryUtil { + + public static Page findPaginated(Pageable pageable, JPAQuery query, boolean determineTotal) { + + long total = -1; + if (determineTotal) { + total = query.clone().fetchCount(); + } + long offset = 0; + if (pageable != null) { + offset = pageable.getOffset(); + query.offset(offset); + query.limit(pageable.getPageSize()); + // applySort(query, pageable.getSort()); + } + List hits = query.fetch(); + if (total == -1) { + total = offset + hits.size(); + } + return new PageImpl<>(hits, pageable, total); + } +} diff --git a/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/utils/StringUtils.java b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/utils/StringUtils.java new file mode 100644 index 0000000..effc93c --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/java/com/devonfw/quarkus/productmanagement/utils/StringUtils.java @@ -0,0 +1,8 @@ +package com.devonfw.quarkus.productmanagement.utils; + +public class StringUtils { + public static boolean isEmpty(String str) { + + return (str == null || "".equals(str)); + } +} diff --git a/integration/quarkus-keycloak-authorization/src/main/resources/application.properties b/integration/quarkus-keycloak-authorization/src/main/resources/application.properties new file mode 100644 index 0000000..603fd16 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/resources/application.properties @@ -0,0 +1,26 @@ +# Configuration file +%prod.quarkus.oidc.auth-server-url=https://localhost:8543/realms/quarkus +quarkus.oidc.client-id=backend-service +quarkus.oidc.credentials.secret=secret +quarkus.oidc.tls.verification=none +quarkus.keycloak.devservices.realm-path=quarkus-realm.json +quarkus.oidc.token.issuer=any + +# Enable Policy Enforcement +quarkus.keycloak.policy-enforcer.enable=true +quarkus.keycloak.policy-enforcer.lazy-load-paths=false + +# Disables policy enforcement for a path +quarkus.keycloak.policy-enforcer.paths.1.path=/api/public +quarkus.keycloak.policy-enforcer.paths.1.enforcement-mode=DISABLED + +# Datasource configuration +quarkus.datasource.db-kind=postgresql +quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/demo +quarkus.datasource.username=demo +quarkus.datasource.password=demo + +# Flyway minimal config properties +quarkus.flyway.migrate-at-start=true +quarkus.flyway.baseline-on-migrate=true +quarkus.flyway.baseline-version=0 \ No newline at end of file diff --git a/integration/quarkus-keycloak-authorization/src/main/resources/db/migration/V001__Create_schema.sql b/integration/quarkus-keycloak-authorization/src/main/resources/db/migration/V001__Create_schema.sql new file mode 100644 index 0000000..00b5a31 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/resources/db/migration/V001__Create_schema.sql @@ -0,0 +1,15 @@ + +CREATE SEQUENCE HIBERNATE_SEQUENCE START WITH 1000000; + +-- *** Product catalog *** + +CREATE TABLE Product +( + id BIGSERIAL, + modificationCounter INTEGER DEFAULT 0 NOT NULL, + title VARCHAR (255) NOT NULL, + description VARCHAR (4000), + price DECIMAL (16,2) NOT NULL, + CONSTRAINT PK_Product PRIMARY KEY(id) +); + diff --git a/integration/quarkus-keycloak-authorization/src/main/resources/db/migration/V002__Master_data.sql b/integration/quarkus-keycloak-authorization/src/main/resources/db/migration/V002__Master_data.sql new file mode 100644 index 0000000..e8eacc2 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/main/resources/db/migration/V002__Master_data.sql @@ -0,0 +1,352 @@ +-- Products 350 entries from https://github.com/etano/productner/blob/master/Product%20Dataset.csv +-- Original source: Justifying recommendations using distantly-labeled reviews and fined-grained aspects, Jianmo Ni, Jiacheng Li, Julian McAuley, Empirical Methods in Natural Language Processing (EMNLP), 2019ss +INSERT INTO Product( title, description, price) VALUES ( 'Bose Acoustimass 5 Series III Speaker System - AM53BK', 'Bose Acoustimass 5 Series III Speaker System - AM53BK/ 2 Dual Cube Speakers With Two 2-1/2'' Wide-range Drivers In Each Speaker/ Powerful Bass Module With Two 5-1/2'' Woofers/ 200 Watts Max Power/ Black Finish', 399.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Switcher - SBV40S', 'Sony Switcher - SBV40S/ Eliminates Disconnecting And Reconnecting Cables/ Compact Design/ 4 A/V Inputs With S-Video Jacks/ 1 A/V Output With S-Video (Y/C)Jack/ 2 Audio Output', 49.00); +INSERT INTO Product( title, description, price) VALUES ( 'Bose 27028 161 Bookshelf Pair Speakers In White - 161WH', 'Bose 161 Bookshelf Speakers In White - 161WH/ Articulated Array Speaker Design/ High-Excursion Twiddler Drivers/ Magnetically Shielded/ Priced Per Pair/ White Finish', 158.00); +INSERT INTO Product( title, description, price) VALUES ( 'Denon Stereo Tuner - TU1500RD', 'Denon Stereo Tuner - TU1500RD/ RDS Radio Data System/ AM-FM 40 Station Random Memory/ Rotary Tuning Knob/ Dot Matrix FL Display/ Optional Remote', 375.00); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic Integrated Telephone System - KXTS108W', 'Panasonic Integrated Telephone System - KXTS108W/ 16 Digit LCD With Clock/ Hands Free Speakerphone/ Built-In Data Port/ 10-Station One-Touch Dialing/ 3-Step Ringer Volume/ White Finish', 44.00); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic Hands-Free Headset - KXTCA86', 'Panasonic Hands-Free Headset - KXTCA86/ Comfort Fit And Fold Design/ Noise Cancelling Microphone/ Standard 2.5mm Connection', 14.95); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic Hands Free Headset - KXTCA92', 'Panasonic Hands Free Headset - KXTCA92/ Comfort Fit With Fold Design/ Noise Cancelling Microphone/ Volume Control/ Mute/ Standard 2.5mm Connection', 25.00); +INSERT INTO Product( title, description, price) VALUES ( 'Cuisinart Convection-Oven-Toaster-Broiler With Exact Heat Sensor - TOB165WH', 'Cuisinart Convection-Oven-Toaster-Broiler With Exact Heat Sensor - TOB165WH/ 0.5 Cubic Foot Oven Capacity/ LED Indicators/ Individual Or Combination Settings/ Always Even Shade Control/ 4 Hour Automatic Shut Off/ Slide-Out Crumb Tray/ Includes Broiling Pan/ White Finish', 149.00); +INSERT INTO Product( title, description, price) VALUES ( 'Frigidaire 24'' White Built-In Dishwasher - FDB130WH', 'Frigidaire 24'' FDB130RGS White Built-In Dishwasher - FDB130WH/ Convection Drying System/ QuietSound Sound Insulation Package/ 2 Wash Levels/ Adjustable Rinse Aid Dispenser/ Self Cleaning Filter/ White Finish', 229.00); +INSERT INTO Product( title, description, price) VALUES ( 'Cuisinart Cordless Electric Kettle - KUA17', 'Cuisinart Cordless Electric Kettle - KUA17/ 1-3/4 Quart Capacity/ Automatic Shut-Off/ Indicator Light/ Splash Guard Spout/ Cord Storage In Base/ Chrome Finish', 70.00); +INSERT INTO Product( title, description, price) VALUES ( 'Omnimount Wall Speaker Mount - 20WLBK', 'Omnimount Wall Speaker Mount - 20WLBK/ Stainless Steel Shafts And All Necessary Hardware Included/ Supports Speakers Up To 20 lbs./ Sold As Single / Black Finish', 39.95); +INSERT INTO Product( title, description, price) VALUES ( 'Omnimount Wall Speaker Mount - 20WLWH', 'Omnimount Wall Speaker Mount - 20WLWH/ Stainless Steel Shafts And All Necessary Hardware Included/ Supports Speakers Up To 20 lbs./ Sold as each / White Finish (Photo Showing Black)', 40.00); +INSERT INTO Product( title, description, price) VALUES ( 'Denon Semi-Automatic Turntable - Black Finish - DP29F', 'Denon Semi-Automatic Turntable - DP29F/ Metal Platter/ Built-In RIAA Equalizer/ DC Servo Motor/ 2 Speed 33 + 45 RPM/ Built-In Phono PreAmp', 150.00); +INSERT INTO Product( title, description, price) VALUES ( 'Escort Passport Radar And Laser Detector - Black Finish - 8500', 'Escort Passport X50 Radar And Laser Detector - 8500/ X-Band, K-Band, Ka-Band Operating Bands/ AlGaAs 280 LED Matrix/Text Display Type/ 3-Level Dimming, Plus Dark Mode/ Auto Mute/ City Mode Sensitivity/ Compact Size/ Red Display', 313.95); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Compact Disc Player/Recorder - RCDW500C', 'Sony Compact Disc Player/Recorder - RCDW500C/ 5-CD/Dual Deck With 4x High Speed Dubbing/ CD, CD-R, CD-RW, MP3 Playback Capable/ Super Bit Mapping Recording/ High Speed Finalizing', 299.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus WMS3B Black Weather Resistant Small Speaker Wall Mount - WMS3B', 'Sanus WMS3B Black Small Speaker Wall Mount - WMS3B/ Holds Up To An 8 Pound Speaker/ Multiple Pivot Points/ Weather Resistant For Indoor/Outdoor Use/ Black Finish/ Priced Per Pair', 29.99); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus WMS3S Silver Weather Resistant Small Speaker Wall Mount - WMS3S', 'Sanus WMS3S Silver Small Speaker Wall Mount - WMS3S/ Holds Up To An 8 Pound Speaker/ Multiple Pivot Points/ Weather Resistant For Indoor/Outdoor Use/ Silver Finish/ Priced Per Pair', 29.99); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus Euro Foundations Satellite Speaker Stand - EFSATB', 'Sanus Euro Foundations Satellite Speaker Stand - EFSATB/ Sturdy Base/ Adjustable Pillar And Floor Spikes/ Includes Three Different Speaker Mounting Methods/ Contemporary European Design/ Satin Powder-Coated Black Finish/ Priced Per Pair', 79.99); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus Euro Foundations Satellite Speaker Stand - EFSATS', 'Sanus Euro Foundations Satellite Speaker Stand - EFSATS/ Sturdy Base/ Adjustable Pillar And Floor Spikes/ Includes Three Different Speaker Mounting Methods/ Contemporary European Design/ Satin Powder-Coated Silver Finish/ Priced Per Pair', 79.99); +INSERT INTO Product( title, description, price) VALUES ( 'Escort Cordless Solo Radar Detector - S2E', 'Escort Cordless Solo Radar Detector - S2E/ S2/ 10 Programmable Features/ High-Efficiency Power Management/ Ultra-Performance Laser Protection/ AutoSensitivity Mode/ High Resolution Graphic LCD Display/ Built-In Earphone Jack', 343.95); +INSERT INTO Product( title, description, price) VALUES ( 'Kenwood 6-Disc CD Changer - KDCC669', 'Kenwood 6-Disc CD Changer - KDCC669/ 3-Angle Mounting/ CD, CD-R And CD-RW Playback/ Anti-Vibration Disc Transport/ Compatible With All Kenwood Units With Changer Control', 129.00); +INSERT INTO Product( title, description, price) VALUES ( 'Cuisinart Automatic Brew And Serve Coffeemaker - DTC975BK', 'Cuisinart Automatic Brew And Serve Coffeemaker - DTC975BK/ 12-Cup Double-Wall Insulated Stainless Steel Carafe/ Fully Automatic With 24-Hour Programmability/ Patented Brew-Through And Pour-Through Lid/ Brew Pause Feature/ Automatic Shutoff/ Black And Stainless Steel Finish', 99.95); +INSERT INTO Product( title, description, price) VALUES ( 'Sharp Over The Counter Microwave Oven - R1214SS', 'Sharp Over The Counter Microwave Oven - R1214SS/ 1.5 Cubic Foot Capacity/ 1100 Watts/ 24 Automatic Settings/ 2-Color Lighted LCD/ Smart And Easy Sensor Settings/ Auto-Touch Control Panel/ Stainless Steel Finish', 429.00); +INSERT INTO Product( title, description, price) VALUES ( 'Toshiba Rechargeable 5-Hour Battery Pack - MEDB05LX', 'Toshiba Rechargeable 5-Hour Battery Pack - MEDB05LX/ Works With SDP2500 And SDP2600 Portable DVD Players', 149.99); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Super Audio CD Player - SCDCE595', 'Sony Super Audio CD Player - SCDCE595/ Multi-Channel Super Audio CD Playback Capability/ CD/CD-R/CD-RW Playback Capability/ Multi-Channel Direct Stream Digitial Decoder/ Multi-Channel Management System/ SACD Text/CD Text Capability/ ETA LATE JANUARY 2009', 149.00); +INSERT INTO Product( title, description, price) VALUES ( 'Delonghi Twenty Four Seven Coffee Maker In Black - DC50B', 'Delonghi Twenty Four Seven Coffee Maker - DC50B/ 4-Cup Capacity/ Easy-Access, Washable Filter Basket/ Black Finish', 22.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus Silver LCD Television Turntable - TVLCDS', 'Sanus Silver LCD Television Turntable - TVLCDS/ Holds 13''-30'' Size Televisions/ 360 Degree Rotation/ Silver Finish', 29.99); +INSERT INTO Product( title, description, price) VALUES ( 'Delonghi Twenty Four Seven Coffee Maker - DC50W', 'Delonghi Twenty Four Seven Coffee Maker - DC50W/ 4-Cup Capacity/ Easy-Access, Washable Filter Basket/ White Finish', 22.00); +INSERT INTO Product( title, description, price) VALUES ( 'Universal IR/RF Remote - MX350', 'Universal IR/RF Remote - MX350/ Controls Up To 10 Components/ Extensive Macro Programming/ Memory Back-Up/ One Hand Ergonomics', 149.95); +INSERT INTO Product( title, description, price) VALUES ( 'Universal IR/RF Aeros Remote Control- MX850 - MX850', 'Universal IR/RF Aeros Remote Control- MX850/ Laser Etched Buttons/ Centrally Located Joystick/ Memory Back-Up/ One Hand Ergonomics/ Controls Up To 20 Components', 399.95); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic 5-Pack DVD-RAM Discs - LMAF120LU5', 'Panasonic 5-Pack DVD-RAM Discs - LMAF120LU5/ Slim Cases/ 2-3x Speed/ Single-Sided/ 120 Minute (4.7GB/Non-Cartridge)/ For Video Recording/ 5 Pack', 15.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus 13'' - 30'' VisionMount Flat Panel TV Silver Wall Mount - VMFS', 'Sanus 13'' - 30'' VisionMount Flat Panel TV Silver Wall Mount - VMFS/ Supports Up To 40 lbs/ Easy To Install/ Fingertip Virtual Axis Tilting System/ Silver Finish', 39.99); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Performer 22-1/2'' Charcoal Grill - 841001', 'Weber Performer 22-1/2'' Charcoal Grill - 841001/ Push-Button Igniter/ Porcelain-Enameled Bowl And Lid/ Dual-Purpose Themometer/ Crackproof All-Weather Wheels/ Black Lid Finish/ Assembly Required', 329.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus 13'' - 30'' Flat Panel TV Black Wall Mount - VM1B', 'Sanus 13'' - 30'' Flat Panel TV Black Wall Mount - VM1B/ Tilt And Swivel Motion/ Rigid Extruded Aluminum Construction/ Supports Up To 50 Lbs/ Black Finish', 69.99); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus 15'' - 40'' Flat Panel TV Silver Wall Mount - VM400S', 'Sanus 15'' - 40'' Flat Panel TV Silver Wall Mount - VM400S/ Virtual Axis Tilt Adjustment System/ Hinged Arm Extend From 3.5'' To 20''/ Durable Powder Coated Silver Finish', 219.99); +INSERT INTO Product( title, description, price) VALUES ( 'Delonghi Oil Filters - FK8', 'Delonghi Oil Filters - FK8/ Made For Use With D895UX/ 3 Pack', 18.00); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Performer 22-1/2'' Charcoal Grill - 848001', 'Weber Performer 22-1/2'' Charcoal Grill - 848001/ Push-Button Igniter/ Porcelain-Enameled Bowl And Lid/ Dual-Purpose Themometer/ Crackproof All-Weather Wheels/ Blue Lid Finish/ Assembly Required', 329.00); +INSERT INTO Product( title, description, price) VALUES ( 'Whirlpool 24'' Built-In Dishwasher - DU1055BK', 'Whirlpool 24'' Built-In Dishwasher - DU1055BK/ 14-Five Piece Place Setting Super Capacity Tub/ 5 Level Direct Feed SheerClean Wash System/ 4 Cycles/ AnyWare Plus Silverware Basket/ Quiet Partner I Sound Package/ Energy Star Qualified/ Black Finish', 397.00); +INSERT INTO Product( title, description, price) VALUES ( 'Whirlpool 24'' Built-In Dishwasher - DU1055SS', 'Whirlpool 24'' Built-In Dishwasher - DU1055SS/ 14-Five Piece Place Setting Super Capacity Tub/ 5 Level Direct Feed SheerClean Wash System/ 4 Cycles/ Soak And Scour Option/ AnyWare Plus Silverware Basket/ Quiet Partner I Sound Package/ Energy Star Qualified/ Black On Stainless Finish', 491.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Soft Cyber-Shot Carrying Case - LCSCST', 'Sony Soft Cyber-Shot Carrying Case - LCSCST/ Sturdy Nylon Construction/ Compact And Very Lightweight/ Stylish Black Design', 14.00); +INSERT INTO Product( title, description, price) VALUES ( 'Pioneer XM Digital Satellite Tuner for Pioneer Headunits - GEXP920XM', 'Pioneer XM Digital Satellite Tuner For Pioneer Headunits - GEXP920XM/ SAT Radio Ready/ XM Ready/ Built-In FM Modulator/ 18- Station/ 6- Button Presets/ Magne Mount Installation', 98.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus Universal Projector Ceiling Mount - Black Finish - VMPR1B', 'Sanus Universal Projector Ceiling Mount - VMPR1B/ Designed For DLP And LCD Projectors/ Quick Release Mechanism/ 50 Lbs Capacity/ Black Finish', 129.99); +INSERT INTO Product( title, description, price) VALUES ( 'Kenwood iPod Mobile Interface - KCAIP500', 'Kenwood iPod Mobile Interface - KCAIP500/ Compatible With Most Kenwood Receivers/ Text Display, Multiple Search Mode/ Powers And Charges iPod', 49.99); +INSERT INTO Product( title, description, price) VALUES ( 'Pioneer Wired Marine Remote Control Display - CDMR80D', 'Pioneer Wired Marine Remote Control Display - CDMR80D/ Compatible With Pioneer Headunits/ Satellite Radio Text Indications/ ATT (Volume Attenuator) Button', 149.00); +INSERT INTO Product( title, description, price) VALUES ( 'Bose Second Zone Remote - PMC2', 'Bose Second Zone Remote - PMC2/ Controls Lifestyle 38 Or 48 Media Center/ TV, VCR, Cable Box, Satellite Receiver/ Accesses Digitally Stored CDs In UMusic System', 149.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony DVD-R Recordable Camcorder Media - 3DMR30L1H', 'Sony DVD-R Recordable Camcorder Media 3 Pack - 3DMR30L1H/ 30 Minute, 1.4 GB/ Accucore Technology/ Store Digital Video, Audio And Multimedia Files/ 3 Pack', 9.99); +INSERT INTO Product( title, description, price) VALUES ( 'Sony VAIO Neoprene Laptop Carrying Case - Black Finish - VGPAMC3', 'Sony VAIO Neoprene Laptop Carrying Case - VGPAMC3/ Compatible With VAIO A Series 15'' And FS Series 15.4'' Widescreen Notebooks/ Helps Protect Your Notebook From Scratches, Spills And Dings/ Neoprene Offers Durable And Water-Resistant Protection', 22.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Color Ink Tank - CL41CL', 'Canon Color Ink Tank - CL41CL/ Compatible With The Pixma iP1600, MP170 Printers', 24.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Cyan Ink Tank - Cyan - CLI8C', 'Canon Cyan Ink Tank - CLI8C/ Compatible With The Pixma iP4200, iP5200, iP5200R, iP6600D, MP500, MP800 Printers', 16.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Magenta Ink Tank - Magenta - CLI8M', 'Canon Magenta Ink Tank - CLI8M/ Compatible With The Pixma iP4200, iP5200, iP5200R, iP6600D, MP500, MP800 Printers', 16.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Cyan Photo Ink Cartridge - Cyan - CLI8PC', 'Canon Cyan Photo Ink Cartridge - CLI8PC/ Compatible With The Pixma iP6600D Printer', 16.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Magenta Photo Ink Cartridge - Magenta - CLI8PM', 'Canon Magenta Photo Ink Cartridge - CLI8PM/ Compatible With The Pixma iP6600D Printer', 16.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Yellow Ink Cartridge - Yellow - CLI8Y', 'Canon Yellow Ink Cartridge - CLI8Y/ Compatible With The Pixma iP4200, iP5200, iP5200R, iP6600D, MP500, MP800 Printers', 16.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Black Ink Cartridge - Black - PG40BK', 'Canon Black Ink Cartridge - PG40BK/ Compatible With The Pixma iP1600, MP170 Printers', 19.00); +INSERT INTO Product( title, description, price) VALUES ( 'Pioneer Voice Command Pack - Black Finish - CDVC1', 'Pioneer Voice Command Pack - CDVC1/ Microphone And Steering Wheel Remote Control/ Use Your Voice To Control Navigation, Audio, And Video Functions/ Compatible With AVICN2 And AVICN1', 48.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony VAIO Neoprene Notebook With AC Adapter Case - Black Finish - VGPAMC2', 'Sony VAIO Neoprene Notebook With AC Adapter Case - VGPAMC2/ Helps Protect Your Notebook From Scratches, Spills And Dings/ Neoprene Offers Durable, Water-Resistant Protection/ Fits 17'' Widescreen Notebooks', 24.00); +INSERT INTO Product( title, description, price) VALUES ( 'NetGear ProSafe 24 Port Smart Switch - FS726TP', 'NetGear ProSafe 24 Port Smart Switch - FS726TP/ Two Gigabit Ports Plus Easy Browser/ ProSafe Network Management Software/ Web Based Smart Management Features', 605.00); +INSERT INTO Product( title, description, price) VALUES ( 'Garmin StreetPilot C330 Dash Mount - Black Finish - 0101061300', 'Garmin StreetPilot C330 Dash Mount - 0101061300/ Non-Skid Mount Design/ Fully Portable/ Includes 12/24 Volt Cigarette Lighter Adapter/ Black Finish', 57.00); +INSERT INTO Product( title, description, price) VALUES ( 'Yamaha High Performance Subwoofer - Black Finish - YSTFSW100BK', 'Yamaha High Performance Subwoofer - YSTFSW100BK/ 130 Watts Dynamic Power/ Advanced YST II (Yamaha Active Servo Technology)/ Half Pipe Port/ Powerful 6.5? Multi-Range Driver/ Magnetically Shielded/ 16Hz Ultra Low Frequency Reproduction/ Slim Design/ Black Finish', 150.00); +INSERT INTO Product( title, description, price) VALUES ( 'Netgear ProSafe 16 Port 10/100 Desktop Switch - Purple Finish - FS116P', 'Netgear ProSafe 16 Port 10/100 Desktop Switch - FS116P/ 16 Auto Speed-Sensing 10/100 RJ-45 Ports/ 96 KB Embedded Memory Per Unit', 299.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon High Capacity Color Ink Cartridge - Color Ink - CL51', 'Canon High Capacity Color Ink Cartridge - CL51/ Compatible With Pixma iP6210D, iP6220D, MP150, MP170 And MP450 Printers', 35.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Photo Ink Cartridge - CL52', 'Canon Photo Ink Cartridge - CL52/ Compatible With Pixma iP6210D And iP6220D Printers', 25.00); +INSERT INTO Product( title, description, price) VALUES ( 'Cuisinart Programmable Coffeemaker - Stainless Steel Finish - DCC2000', 'Cuisinart 12-Cup Programmable Coffeemaker - DCC2000/ Fully Programmable/ Removable Coffee Reservoir/ Easy-To-Read Coffee Gauge/ Visible Water Level Indicator/ Removable Drip Tray/ Charcoal Water Filter/ Stainless Steel Finish', 100.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus Center Channel Speaker Mount - Black Finish - VMCC1B', 'Sanus Center Channel Speaker Mount - VMCC1B/ Works With Sanus Models VMSA, VMAA18, VMAA26, VMDD26 And VMCM1/ Easy To Install/ Mounting Hardware Included/ Black Finish', 99.99); +INSERT INTO Product( title, description, price) VALUES ( 'Netgear RangeMax Wireless Access Point - White Finish - WPN802NA', 'Netgear RangeMax Wireless Access Point - WPN802NA/ Improves Performance Of Existing Legacy 802.11b And 802.11g Wireless Devices Up To 50 Percent/ Wired Equivalent Privacy (WEP) 64-Bit,128-Bit Encryption/ Wi-Fi Protected Access (WPA, Pre-Shared Key)', 130.00); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Q 300 Liquid Propane Outdoor Grill - 426001', 'Weber Q 300 Liquid Propane Outdoor Grill - 426001/ Liquid Propane Fuel Type/ Two Burners For Direct And Indirect Cooking/ Thermometer Built Into The Lid/ Regulator Hose/ 12-Pound Turkey Capacity/ Cart Included/ Cast Aluminum Finish/ Assembly Required', 349.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Lightweight Tripod - Black Finish - VCTR100', 'Sony Lightweight Tripod - VCTR100/ Lightweight And Portable/ Expands From 14'' To 39''/ 3-Way Panhead Function/ Black Finish', 34.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus VMAV Black VisionMount Component Wall Shelf VMAVB In Black - VMAVB', 'Sanus VMAV Black VisionMount Component Wall Shelf - VMAVB/ Single Wall Mount Shelf For Audio-Video Component/ Metal V Arm/ Black Finish', 34.99); +INSERT INTO Product( title, description, price) VALUES ( 'Sony PlayStation 2 DUALSHOCK 2 Analog Controller - Emerald Finish - 711719706205', 'Sony PlayStation 2 DUALSHOCK 2 Analog Controller - 711719706205/ Analog Pressure Sensitivity On All Action Buttons/ Built-In DUALSHOCK Vibration Function/ Twin Analog Control Sticks/ Intelligent Self-Calibrating Analog System/ Emerald Finish', 24.99); +INSERT INTO Product( title, description, price) VALUES ( 'Sony PlayStation 2 8MB Memory Card - Black Finish - 711719702702', 'Sony PlayStation 2 8MB Memory Card - 711719702702/ Save And Load High Scores, Positions And Replays/ MagicGate Encryption/ Black Finish', 24.99); +INSERT INTO Product( title, description, price) VALUES ( 'Sony PlayStation 2 8MB Memory Card (2 Pack) - Red/Blue Finish - 711719706700', 'Sony PlayStation 2 8MB Memory Card (2 Pack) - 711719706700/ Save And Load High Scores, Positions And Replays/ MagicGate Encryption/ Red/Blue Finish', 34.99); +INSERT INTO Product( title, description, price) VALUES ( 'Universal RF Series MasterControl Remote Control - RF20', 'Universal RF Series MasterControl Remote Control ? RF20/ Control Up To 10 Components/ 432 MacroPower Buttons/ Customizable LCD Screen/ Pre-Programmed Codes/ Learning Capable/ SimpleSound/ Fully Backlit Keypad/ DVD Guide', 79.99); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic Network Camera - White Finish - BLC1A', 'Panasonic Network Camera - BLC1A/ Automatic Network Configuration/ Built-In Calendar Timer/ Auto Time Adjustment With NTP/ Up To 10x Digital Zoom/ Multi-Language Interface/ White Finish', 99.00); +INSERT INTO Product( title, description, price) VALUES ( 'Pioneer 6.5'' 2-Way Marine White Speakers - TSMR1640', 'Pioneer 6.5'' 2-Way Marine Speakers - TSMR1640/ 160 Watts Maximum Power Handling (30 Watts Nominal)/ 1-1/8'' Poly-Ether Imide Dome Tweeter With Magnetic Fluid And Equalizer/ Tinsel Lead Wire And Terminals Are Gold-Plated For Extra Protection', 120.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple USB Modem - White Finish - MA034ZA', 'Apple USB Modem - MA034ZA/ Supports Caller Wake On Ring, Telephone Answering (V.253), Modem On Hold/ V.92 Software Support', 54.00); +INSERT INTO Product( title, description, price) VALUES ( 'Linksys EtherFast 4124 24-Port Ethernet Switch - EF4124', 'Linksys EtherFast 4124 24-Port Ethernet Switch - EF4124/ 24 Autosensing 10/100 Full Duplex, Auto MDI/MDI-X Ports/ Up To 200Mbps/ Address Learning, Aging And Data Flow Control/ Compact Size', 119.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony DVD Remote Control For PS2 - Black Finish - 711719707608', 'Sony DVD Remote Control For PS2 - 711719707608/ Works As A Full-Featured Standard Controller/ Performs Audio Track Selection, Subtitle Display And Multiangle Options/ Designed To Match The Sleek Look Of PlayStation 2', 19.00); +INSERT INTO Product( title, description, price) VALUES ( 'Nikon 55-200MM Zoom-Nikkor Lens Accessory - 2156', 'Nikon 55-200MM Zoom-Nikkor Lens Accessory - 2156/ 3.6X Zoom/ 55 - 200MM/ Silent Wave Motor/ Two ED Glass Elements/ Focus Mode Switch', 199.00); +INSERT INTO Product( title, description, price) VALUES ( 'Waring Professional Cool-Touch Deep Fryer - Black/Stainless Steel Finish - DF100', 'Waring Professional Cool-Touch Deep Fryer - DF100/ Large Frying Basket/ 60-Minute Timer/ Removable Control Panel/ Unique Heating Element/ Breakaway Cord/ LED Power Indicators', 70.00); +INSERT INTO Product( title, description, price) VALUES ( 'Denon Fully Automatic Analog Turntable - DP300F', 'Denon Fully Automatic Analog Turntable - DP300F/ Removable Headshell/ Automatic Startup/ Built-In Phono Equalizer/ DC Servo Motor And Belt Drive System/ MM Cartridge', 329.00); +INSERT INTO Product( title, description, price) VALUES ( 'Terk Mini Tuner Cartridge For XM Ready Home Products - CNP2000', 'Terk Mini Tuner Cartridge For XM Ready Home Products - CNP2000/ Connects To Any Home Or Portable Audio Product With The XM Ready Logo', 29.00); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic Plain Paper Fax/Copier With Cordless Phone Answering System - Grey Finish - KXFG2451', 'Panasonic Plain Paper Fax/Copier With Cordless Phone Answering System - KXFG2451/ Automatic Fax/Phone Switching/ 2.4GHz GigaRange Cordless Handset With Handset Speakerphone/ Voice Enhancer Technology/ All-Digital Answering System (18 Min)/ Navigator Key With 2-Line Display', 119.00); +INSERT INTO Product( title, description, price) VALUES ( 'Omnimount Moda 2 Shelf Wall Furniture - MWFS', 'Omnimount Moda 2 Shelf Wall Furniture - MWFS/ Modular Design/ Integrated Cable Management/ Tempered Glass Shelves', 299.00); +INSERT INTO Product( title, description, price) VALUES ( 'Terk Mini Tuner Home Dock For XM Ready Home Products - Black Finish - CNP2000H', 'Terk Mini Tuner Home Dock For XM Ready Home Products - CNP2000H/ Comes Complete With The Docking Station, Protective Cover And A Window Sill Mount Antenna/ Interfaces To Existing And Future XM Ready Products', 30.00); +INSERT INTO Product( title, description, price) VALUES ( 'Denon 5-Disc CD Auto Changer - Black Finish - DCM290', 'Denon 5-Disc CD Auto Changer - DCM290/ CD-R/RW Playback/ Advanced Multilevel Noise Shaping DAC/ Digital Filter/ 3-Mode Random Playback/ Intelligent Disc Scan/ Music Calendar Display/ Black Finish', 249.00); +INSERT INTO Product( title, description, price) VALUES ( 'Denon 5 Disc CD Player - Black Finish - DCM390', 'Denon 5 Disc CD Player - DCM390/ CD-R/RW Playback/ MP3, WMA And HDCD Decoding/ Advanced Multilevel Noise Shaping DAC/ 8 Times Oversampling Digital Filter/ 3 Mode Random Playback/ Intelligent Disc Scan/ 20 Selection Music Calendar Display/ Black Finish', 349.00); +INSERT INTO Product( title, description, price) VALUES ( 'Denon Progressive Scan Universal DVD Player - DVD2930CI', 'Denon Progressive Scan Universal DVD Player - DVD2930CI/ AC 120 Volts/ 60 Hertz/ 45 Watts/ Infrared Pulse Remote Control', 849.00); +INSERT INTO Product( title, description, price) VALUES ( 'Netgear Prosafe 16 Port 10/100 Rackmount Switch - Black Finish - JFS516NA', 'Netgear Prosafe 16 Port 10/100 Rackmount Switch - JFS516NA/ Sixteen Switched Ports Provide Private Bandwidth For PCs, Servers Or Hubs/ Store-And-Forward Packet Switching/ Compatible With All Major Network Software/ Auto Detects Speed And Duplex/ Black Finish', 131.00); +INSERT INTO Product( title, description, price) VALUES ( 'Terk XM Outdoor Home Antenna - Grey Finish - XM6', 'Terk XM Outdoor Home Antenna - XM6/ Universal Mounting Roof, Wall Or Mast/ For Use With Single-Input Receivers/ Connects RG6 Cable For Easy Routing Up To 100 Ft. (Optimal Disatnce 75 Ft.)', 80.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus 9'' - 17'' VisionMount Series Under Cabinet Flat Panel TV Silver Wall Mount - VMUC1S', 'Sanus 9'' - 17'' VisionMount Series Under Cabinet Flat Panel TV Silver Wall Mount - VMUC1S/ Tilting Motion/ Swivels Left And Right/ Universal Mounting Bracket/ Easy To Install/ Silver Finish', 99.99); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus 15'' - 40'' VisionMount Flat Panel TV Black Wall Mount - MT25B1', 'Sanus 15'' - 40'' VisionMount Flat Panel TV Black Wall Mount - MT25B1/ Solid Heavy-Gauge Steel Construction/ Durable Powder-Coated Finish/ Virtual Axis Tilt Adjustment System/ Up to 100 Lbs Support/ Black Finish', 99.99); +INSERT INTO Product( title, description, price) VALUES ( 'Garmin GPS Carrying Case - Black Finish - 0101070400', 'Garmin GPS Carrying Case - 0101070400/ Protect Your GPS By Absorbing The Impact From Routine Drops/ Prevent Damage From Scratches And Spills', 30.00); +INSERT INTO Product( title, description, price) VALUES ( 'Tech Craft Avalon Series TV Stand - Black Finish - ABS32', 'Tech Craft Avalon Series TV Stand - ABS32/ 32'' Wide ''Tall Boy'' For Smaller Screen Flat Panels/ Molded Top Gives It An Elegant Appearance/ Framed Doors To Conceal Components/ Black Finish', 249.00); +INSERT INTO Product( title, description, price) VALUES ( 'Tech Craft Avalon Series TV Stand - SWP48', 'Tech Craft Avalon Series TV Stand - SWP48/ 48'' Wide Credenza For Flat Panel TVs And DLPs/ 260 Lbs TV Weight Capacity/ 50 Lbs Shelf Weight Capacity/ Wood Veneer Finish', 299.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus 15'' - 40'' VisionMount Flat Panel TV Black Wall Mount - MF110B', 'Sanus 15'' - 40'' VisionMount Flat Panel TV Black Wall Mount - MF110B/ Mounts Within 2.5'' Of Wall/ Holds Up To 100 Lbs/ Powder Coated Black Finish', 179.99); +INSERT INTO Product( title, description, price) VALUES ( 'Garmin Nuvi 360 010-10815-00 Black Replacement Vehicle Suction Cup Mount - 0101081500', 'Garmin Nuvi 360 010-10815-00 Black Replacement Vehicle Suction Cup Mount - 0101081500/ Compatible With The Nuvi 360/ It Is A Replacement/ Black Finish', 39.00); +INSERT INTO Product( title, description, price) VALUES ( 'Garmin Nuvi 360 010-10723-06 Black 12 Volt Adapter Cable - 0101072306', 'Garmin Nuvi 360 010-10723-06 Black 12 Volt Adapter Cable - 0101072306/ Compatible With The Nuvi 350 And 360/ It Is A Replacement/ Black Finish', 47.00); +INSERT INTO Product( title, description, price) VALUES ( 'Garmin Nuvi 660 010-10747-03 Black 12 Volt Adapter Cable - 0101074703', 'Garmin Nuvi 660 010-10747-03 Black 12 Volt Adapter Cable - 0101074703/ Compatible With The Nuvi 660/ Black Finish', 39.00); +INSERT INTO Product( title, description, price) VALUES ( 'Garmin 010-10702-00 Black GA 25MCX Remote GPS Antenna - 0101070200', 'Garmin 010-10702-00 Black GA 25MCX Remote GPS Antenna - 0101070200/ Built-In Magnetic Mount/ Includes A Cable Over 8 Feet Long And MCX Connector', 30.00); +INSERT INTO Product( title, description, price) VALUES ( 'Garmin 010-10823-00 Black Nuvi 660 Vehicle Suction Cup Mount - 0101082300', 'Garmin 010-10823-00 Black Nuvi 660 Vehicle Suction Cup Mount - 0101082300/ Replacement Suction Cup Mount/ Compatible With Nuvi 660/ Black Finish', 46.00); +INSERT INTO Product( title, description, price) VALUES ( 'Universal MRF-350 RF Black Base Station - MRF350', 'Universal MRF-350 RF Black Base Station - MRF350/ No More Pointing/ RF Addressable/ IR Routing/ Expand Operating Range Up To 100 Feet/ Compatible With MX-3000, TX-1000, MX-950 And MX-900 Only/ Black Finish', 250.00); +INSERT INTO Product( title, description, price) VALUES ( 'Bose In-Ear Black Headphones - BOSEIE', 'Bose In-Ear Black Headphones - BOSEIE/ Comfortable In-Ear Design/ TriPort Acoustic Headphone Structure/ S, M And L Removable Silicone Tips/ Carrying Case/ Black Finish', 99.95); +INSERT INTO Product( title, description, price) VALUES ( 'Nyko PlayStation 3 Dual Charger AC - 743840830153', 'Nyko PlayStation 3 Dual Charger AC - 743840830153/ Charge 2 Wireless PS3 Controllers From A Standard Wall Outlet/ Collapsable Prongs For Easy Storage/ Charges Other Mini USB Devices', 24.99); +INSERT INTO Product( title, description, price) VALUES ( 'Nyko PlayStation 3 ChargeLink USB Charging Cable - 743840830009', 'Nyko PlayStation 3 ChargeLink USB Charging Cable - 743840830009/ Charge And Play At The Same Time/ Unique Woven Cable Shielding Improves Cable Durability/ 10 Ft Cable', 14.99); +INSERT INTO Product( title, description, price) VALUES ( 'Linksys Wireless-G VPN Broadband Silver Router - WRV54G', 'Linksys Wireless-G VPN Broadband Silver Router - WRV54G/ Built-In VPN Endpoint Capability/ Securely Connect Up To 50 Remote Or Traveling Users To Your Office Network Via VPN/ Hotspot Ready With Subscriber Registration, Authorization And Authentication Functions/ Silver Finish', 149.00); +INSERT INTO Product( title, description, price) VALUES ( 'Bose SL2 Wireless Black Surround Link - SL2WIRELESS', 'Bose SL2 Wireless Black Surround Link - SL2WIRELESS/ Transmitter And Receiver Work On A Radio Frequency Signal Effective From Up To 30 Feet In The Same Room/ Compatible With All Lifestyle Systems, CD-Based Models And All 5.1-Channel Acoustimass Home Entertainment Systems/ Black Finish', 249.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Digital Photo Printer Paper 120 Pack - SVMF120P', 'Sony Digital Photo Printer Paper 120 Pack - SVMF120P/ 4'' x 6'' Print Paper With Snap-Off Edges/ Super Coat 2 Protective Lamination/ Water Damage And Fingerprint Resistant Prints/ 120 Sheets Of Paper And Print Ribbon/ For DPPF Series Digital Photo Printers Only', 35.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon PGI-5BK Black Ink Tank Cartridge - PGI5BK', 'Canon PGI-5BK Black Ink Tank Cartridge - PGI5BK/ Smudge Resistant/ Resists Smearing Caused By Highlighters/ Smudge Resistant/ Pigment Ink Formulation For Long Lasting Prints', 16.00); +INSERT INTO Product( title, description, price) VALUES ( 'Lowepro SlingShot 200 AW Digital Camera Back Pack - SLINGSHOT200AW', 'Lowepro SlingShot 200 AW Digital Camera Back Pack - SLINGSHOT200AW/ Holds A SLR With Attached Compact Zoom Lens Plus 3-4 Extra Lenses Or Flash Unit/ Water-Resistant Micro Fiber/ Ripstop Nylon/ Includes A Built-In Memory Card Puch, Micro Fiber LCD Cloth And Two Organizer Pockets', 89.95); +INSERT INTO Product( title, description, price) VALUES ( 'Tech Craft ABS48 Antique Black Avalon Series 48'' TV Stand - ABS48', 'Tech Craft ABS48 Antique Black Avalon Series 48'' TV Stand - ABS48/ For Flat Panels And DLP TV?s/ Molded Top And Shaped Skirt/ Framed Doors/ Antique Black Distressed Finish', 299.00); +INSERT INTO Product( title, description, price) VALUES ( 'Garmin 010-10723-03 Nuvi Suction Cup Mount - 0101072303', 'Garmin 010-10723-03 Nuvi Suction Cup Mount - 0101072303/ Replacement Suction Cup Mount/ Compatible With Garmin Nuvi 300/310/350/360 GPS System/ Black Finish', 39.00); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Stainless Steel Summit S-650 Liquid Propane Grill - 1780001', 'Weber Stainless Steel Summit S-650 Liquid Propane Grill - 1780001/ 6 Stainless Steel Burners/ 60,000 BTU-Per-Hour Input/ 12,000 BTU-Per-Hour Input Side Burner/ 10,600 BTU-Per-Hour Rotisserie Burner/ 8,000 BTU-Per-Hour Input Smoker Burner/ Snap-Jet Individual Burner Ignition System/ 3/8-Inch Diameter Stainless Steel Rod Cooking Grates/ Stainless Steel Flavorizer Bars/ Stainless Steel Finish/ Liquid Propane Model (LP Tank Not Included)/ Assembly Required', 1999.00); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Stainless Steel Genesis S-310 Liquid Propane Grill - 3770001', 'Weber Stainless Steel Genesis S-310 Liquid Propane Grill - 3770001/ 3 Stainless Steel Burners/ 42,000 BTU-Per-Hour Input/ Electronic Crossover Ignition System/ 7MM Diameter Stainless Steel Rod Cooking Grates/ Stainless Steel Flavorizer Bars/ Stainless Steel Finish/ Liquid Propane Model (LP Tank Not Included)/ Assembly Required', 899.00); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Stainless Steel Genesis S320 LP Grill - 3780001', 'Weber 3780001 Stainless Steel Genesis S-320 Liquid Propane Grill - 3780001/ 3 Stainless Steel Burners/ 42,000 BTU-Per-Hour Input/ 12,000 BTU-Per-Hour Input Side Burner/ Electronic Crossover Ignition System/ 7MM Diameter Stainless Steel Rod Cooking Grates/ Stainless Steel Flavorizer Bars/ Stainless Steel Finish/ Liquid Propane Model (LP Tank Not Included)/ Assembly Required', 949.00); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Stainless Steel Genesis S320 Natural Gas Grill - 3880001', 'Weber Stainless Steel Genesis S-320 Natural Gas Grill - 3880001/ 3 Stainless Steel Burners/ 42,000 BTU-Per-Hour Input/ 12,000 BTU-Per-Hour Input Side Burner/ Electronic Crossover Ignition System/ 7MM Diameter Stainless Steel Rod Cooking Grates/ Stainless Steel Flavorizer Bars/ Stainless Steel Finish/ Natural Gas Model/ Assembly Required', 969.00); +INSERT INTO Product( title, description, price) VALUES ( 'Kenwood KCA-IP300V iPod Video Direct Cable - KCAIP300V', 'Kenwood KCA-IP300V iPod Video Direct Cable - KCAIP300V/ iPod Video Direct Cable For DNX7100, DDX7019 And KVT719DVD Indash Monitors', 49.99); +INSERT INTO Product( title, description, price) VALUES ( 'Sony SCPH-98046 PlayStation 3 Blu-Ray DVD Remote Control - 711719804604', 'Sony SCPH-98046 PlayStation 3 Blu-Ray DVD Remote Control - 711719804604/ Full Access To The PlayStation 3 Systems Disc Features/ Can Be Used Without Having To Point Directly At The System', 24.99); +INSERT INTO Product( title, description, price) VALUES ( 'Tripp-Lite PV375 PowerVerter 375-Watt Ultra-Compact Inverter - PV375', 'Tripp-Lite PV375 PowerVerter 375-Watt Ultra-Compact Inverter - PV375/ 12V DC Input/ 120V AC Output/ 2 Outlets/ 375 Watts Continuous Output/ 600 Watts Peak Output/ Convenient Cigarette Lighter Plug', 49.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sirius FMDA25 Wired FM Modulation Relay - FMDA25', 'Sirius FMDA25 Wired FM Modulation Relay - FMDA25/ 2 Ft FM Antenna Cable/ 19 Ft 4 In Mini-Jack Cable', 20.00); +INSERT INTO Product( title, description, price) VALUES ( 'Electrolux Oxygen 3 Canister HEPA H12 Filter - EL012A', 'Electrolux Oxygen 3 Canister HEPA H12 Filter - EL012A/ Retains 99.5% Of Dust And Other Irritants/ 1 Filter Per Package', 19.99); +INSERT INTO Product( title, description, price) VALUES ( 'Fellowes Confetti Cut Shredder - W11C', 'Fellowes Confetti Cut Shredder - W11C/ Shreds Up To 11 Sheets Per Pass/ Safely Shreds Staples And Credit Cards/ 9'' Paper Entry Accepts Any Standard Or Legal Size Document/ Safety Interlock Switch Automatically Powers Off When Shredder Head Is Lifted', 79.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony VAIO VGP-PRSZ1 SZ Series Docking Station - VGPPRSZ1', 'Sony VAIO VGP-PRSZ1 SZ Series Docking Station - VGPPRSZ1/ Compatible With SZ Series Notebooks/ Expand Connectivity To Your Notebook/ 3 USB 2.0, Gigabit Ethernet, VGA, DVI-D And DC In/ Black Finish', 199.00); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Genesis E-310 Liquid Propane Black Outdoor Grill - 3741001', 'Weber Genesis E-310 Liquid Propane Black Outdoor Grill - 3741001/ 3 Stainless Steel Burners/ 42,000 BTU Per-Hour Input/ Electronic Crossover Ignition System/ Porcelain Enameled Cast Iron Cooking Grates/ 2 Stainless Steel Work Surfaces/ Center Mounted Thermometer/ Cast Aluminum End Caps/ Black Finish/ Liquid Propane Model (LP Tank Not Included)/ Assembly Required', 699.00); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Genesis S-310 Natural Gas Stainless Steel Outdoor Grill - 3870001', 'Weber Genesis S-310 Natural Gas Stainless Steel Outdoor Grill - 3870001/ 3 Stainless Steel Burners/ 42,000 BTU-Per-Hour Input/ Electronic Crossover Ignition System/ Center Mounted Thermometer/ Cast Aluminum End Caps/ 2 Heavy Duty Front Locking Casters/ Stainless Steel Finish/ Assembly Required', 919.00); +INSERT INTO Product( title, description, price) VALUES ( 'DeLonghi Magnifica Super-Automatic Espresso/Coffee Machine - ESAM3300', 'DeLonghi Magnifica Super-Automatic Espresso/Coffee Machine - ESAM3300/ Stainless-Steel Removable Double Boiler/ Instant Reheat/ Removable Water Tank And Bean Container/ Integrated Burr Grinder/ Cappuccino System/ Cup Tray/ Silver Finish', 799.95); +INSERT INTO Product( title, description, price) VALUES ( 'Yamaha Silver USB Powered Stereo Speaker - NXU10SIL', 'Yamaha NX-U10 Silver USB Powered Stereo Speaker - NXU10SIL/ 20 Watts Output Power/ PowerStorage Circuit/ SR-Bass (Swing Radiator Bass) Technology/ Magnetic Shielding/ Mac And Windows Compatible/ Silver Finish', 180.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon FAX-JX200 Inkjet Fax Machine - FAXJX200', 'Canon FAX-JX200 Inkjet Fax Machine - FAXJX200/ Automatic Document Feeder/ Produce B&W Copies With Clear, Sharp Text/ One-Touch Dialing/ Ultra-High Quality (UHQ) Image Processing Technology/ 600 x 600 Dpi Copy Resolution/ White Finish', 78.00); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Genesis E-310 Natural Gas Black Outdoor Grill - 3841001', 'Weber Genesis E-310 Natural Gas Black Outdoor Grill - 3841001/ 3 Stainless Steel Burners/ 42,000 BTU Per-Hour Input/ Electronic Crossover Ignition System/ Porcelain Enameled Cast Iron Cooking Grates/ 2 Stainless Steel Work Surfaces/ Center Mounted Thermometer/ Cast Aluminum End Caps/ Black Finish/ Assembly Required', 719.00); +INSERT INTO Product( title, description, price) VALUES ( 'Weber 3758301 Blue Genesis EP-320 LP Gas Grill - 3758301', 'Weber 3758301 Blue Genesis EP-320 LP Gas Grill - 3758301/ 3 Seamless Stainless Steel Burners/ 42,000 BTU-Per-Hour Input/ Crossover Ignition System/ Stainless Steel Flavorizer Bars And Grates/ Cast Aluminum End Caps/ Centermounted Thermometer/ Blue Finish/ Liquid Propane Model (LP Tank Not Included)/ Assembly Required', 749.00); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic KX-TG6702B 5.8 GHz FHSS GigaRange Expandable Black Cordless Phone System - KXTG6702B', 'Panasonic KX-TG6702B 5.8 GHz FHSS GigaRange Expandable Black Cordless Phone System - KXTG6702B/ All-Digital Answering System/ LCD Call Counter/ Speakerphone/ Navigator Key/ Up To 8 Handsets With Just One Phone Jack/ Line Status Indicator/ Voice Scramble/ Handset Locator/ Volume Control/ Black Finish', 197.00); +INSERT INTO Product( title, description, price) VALUES ( 'Fellowes MicroShred Shredder - MS450CS', 'Fellowes MS-450CS MicroShred Shredder - MS450CS/ 4.5 Gallons Basket Capacity/ Can Shred Paper, CDs, Credit Cards, Staples, Paper Clips/ Motor Reverse/ Interlock Switch/ Safe Sense/ 7 Sheet Capacity', 189.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony MRW62E/S1/181 17-In-1 External USB Memory Card Reader - MRW62ES1181', 'Sony MRW62E/S1/181 17-In-1 External USB Memory Card Reader - MRW62ES1181/ Supports 17 Different Memory Card Formats/ Works With Macintosh And Windows Computers/ Easy Hi-Speed USB 2.0 Connection/ Black Finish', 29.00); +INSERT INTO Product( title, description, price) VALUES ( 'Griffin Elevator Brushed Aluminum Laptop Stand - 1093CURV2', 'Griffin Elevator Brushed Aluminum Laptop Stand - 1093CURV2/ Elevates Laptop Screen 5.5'' While Providing Valuable Desktop Real Estate For Your Keyboard And Mouse/ Keeps Laptop Cool With 360 Degrees Of Air Circulation/ Compatible With Mac Or PC Laptop', 39.00); +INSERT INTO Product( title, description, price) VALUES ( 'Escort Passport 9500I Radar And Laser Detector - 9500I', 'Escort Passport 9500I Radar And Laser Detector - 9500I/ 360-Degree Radar And Laser Detection/ Blistering All-Band Protection/ GPS-Powered Truelock Filter/ Immune To The VG-2 ''Detector-Detector''/ Built-In Earphone Jack/ Red Display', 449.95); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic Countertop Microwave Oven In Black - NNSN667BK', 'Panasonic NN-SN667B Countertop Microwave Oven In Black - NNSN667BK/ 1.2 Cubic Foot/ 1300 Watts High Power/ 10 Power Levels/ 5 Cooking Stages/ Quick Minute/ One-Touch Sensor Cooking/ Inverter Turbo Defrost/ Multi-Lingual Menu Action Screen/ Popcorn Key/ Black Finish', 129.00); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic Countertop Microwave Oven In White - NNSN667WH', 'Panasonic NN-SN667W Countertop Microwave Oven In White - NNSN667WH/ 1.2 Cubic Foot/ 1300 Watts High Power/ 10 Power Levels/ 5 Cooking Stages/ Quick Minute/ One-Touch Sensor Cooking/ Inverter Turbo Defrost/ Multi-Lingual Menu Action Screen/ Popcorn Key/ White Finish', 129.00); +INSERT INTO Product( title, description, price) VALUES ( 'Samsung DLP TV Stand In Black - TR72BX', 'Samsung DLP TV Stand In Black - TR72BX/ Designed To Fit Samsung HLT7288 HLT7288, HL72A650, and HL67A650 Television Sets/ Tempered 6mm Tinted Glass Shelves/ Wide Audio Storage Shelves To Accommodate 4 Or More Components/ Wire Management System/ Easy To Assemble/ High Gloss Black Finish', 369.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Black Active Speaker System - SRSA212BK', 'Sony Black Active Speaker System - SRSA212BK/ Designed For Your Portable Audio Or PC/ Built-In Mega Bass/ On/Off Switch With Volume Control/ Magnetically Shielded/ Black Finish', 29.00); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic Expandable Digital Cordless DECT 6.0 Handset In Silver - KXTGA101S', 'Panasonic Expandable Digital Cordless DECT 6.0 Handset In Silver - KXTGA101S/ Expansion Handset And Charger Select 1000 Series Phone Systems/ Big Buttons/ Built-In Clock With Alarm/ Silver Finish', 39.00); +INSERT INTO Product( title, description, price) VALUES ( 'Tech Craft Dark Cherry Veneto Series TV Stand - SWP60', 'Tech Craft Dark Cherry Veneto Series TV Stand - SWP60/ 60'' Wide Credenza For Flat Panel TV?s And DLP?s/ Center Channel Compartment And Storage/ 260 Lbs TV Capacity/ 50 Lbs Shelf Capacity/ Dark Cherry Wood Veneer Finish', 399.00); +INSERT INTO Product( title, description, price) VALUES ( 'Haier 13'' Silver Tube TV - HTR13', 'Haier 13'' Silver Tube TV - HTR13/ Built-In Atsc/Ntsc Tuner/ Video Noise Reduction/ Multi Picture Modes/ Multilingual Display/ Digital Comb Filter/ Component Video Input/ Front AV Jacks/ Silver Finish', 124.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Memory Stick USB Adaptor - MSACUS40', 'Sony Memory Stick USB Adaptor - MSACUS40/ Quickly Transfer Image And Data To A PC/ Transfer Speed Up To 80Mbps/ Compatible With All Memory Stick Media', 29.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Clip-On Black Headphones - MDRQ68LW', 'Sony Clip-On Black Headphones - MDRQ68LW/ Lightweight And Thin Body For Stable Fitting/ Retractable Silent Cord/ 3.3 Ft. Cord/ Gold Plated Mini-Plug/ Black Finish', 29.00); +INSERT INTO Product( title, description, price) VALUES ( 'Netgear ProSafe 24-Port Smart Switch - GS724TP', 'Netgear ProSafe 24-Port Smart Switch - GS724TP/ 24 10/100/1000 Ports That Support 802.3af PoE/ 2 Combo Gigabit Copper/SFP Slots/ Web-Based Configuration/ Password Access Control/ Purple Finish', 780.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sharp Over The Counter White Microwave Oven - R1211WH', 'Sharp Over The Counter White Microwave Oven - R1211WH/ 1.5 Cu. Ft. Capacity/ 1100 Watts/ 19 Automatic Settings/ 2-Color Lighted LCD/ Smart And Easy Sensor Settings/ Auto-Touch Control Panel/ White Finish', 269.00); +INSERT INTO Product( title, description, price) VALUES ( 'Monster iFreePlay Cordless Headphones For iPod Shuffle - AISHHPHONE', 'Monster iFreePlay Cordless Headphones For iPod Shuffle - AISHHPHONE/ Wrap-Around Design With No Need For Clips, Armbands Or Pockets/ Easy Access To All iPod Shuffle Controls/ Folds For Compact Storage/ Black With Silver Finish', 48.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Black Ink Cartridge - PG50', 'Canon Black Ink Cartridge - PG50/ Pigment Ink Formulation For Long Lasting Prints/ Resists Smearing Caused By Highlighters/ Smudge Resistant/ Ink Remaining Notification Technology/ Crisp Laser Text-Quality Text/ Black Ink', 29.00); +INSERT INTO Product( title, description, price) VALUES ( 'Audiovox Xpress XM Satellite Radio FM Direct Adapter - XMFM1', 'Audiovox Xpress XM Satellite Radio FM Direct Adapter - XMFM1/ Switches Between FM Radio Antenna And Your XM Satellite Radio Receiver', 25.00); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic NNSD797S Stainless Steel Countertop Microwave Oven - NNSD797SS', 'Panasonic NNSD797S Stainless Steel Countertop Microwave Oven - NNSD797SS/ 1.6 Cu. Ft. Capacity/ 1250W Output Power/ 10 Power Levels/ 5 Cooking Stages/ One-Touch Sensor Cooking Or Heating/ Timer/ Stainless Steel Finish', 199.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple Mac Mini 1.83GHz Intel Core 2 Duo Computer - MB138LLA', 'Apple Mac Mini 1.83GHz Intel Core 2 Duo Computer - MB138LLA/ 1.83GHz Intel Core 2 Duo/ 1GB Memory/ 80GB Hard Drive/ 24x Combo Drive/ Built-In Speakers/ Four USB 2.0 Ports And One FireWire 400 Port/ Mac OS X v10.5 Leopard', 599.00); +INSERT INTO Product( title, description, price) VALUES ( 'Denon 7.1 Channel Home Theater MultiMedia A/V Receiver With Networking And WiFi - AVR4308CI', 'Denon 7.1 Channel Home Theater MultiMedia AV Receiver With Networking And WiFi In Black - AVR4308CI/ 140 Watts x 7 Channels/ Wi-Fi And Network Capable/ Worlds First Vista Certified Receiver/ HD Radio And XM Ready Tuning/ Dolby TrueHD And DTS-HD Master Audio/ DDSC-HD Digital Dynamic Discrete Surround Circuitry/ Expanded HDMI v1.3a Ports/ Dual Remotes/ Black Finish', 2699.00); +INSERT INTO Product( title, description, price) VALUES ( 'Denon 7.1 Channel Home Theater MultiMedia A/V Receiver With Networking In Black - AVR3808CI', 'Denon 7.1 Channel Home Theater MultiMedia AV Receiver With Networking In Black - AVR3808CI/ 130 Watts x 7 Channels/ Fully Assignable Channels For Bi-Amping/ Rear Panel RS-232C And Ethernet Ports/ Network Capable/ XM Ready Tuning/ Dolby TrueHD And DTS-HD Master Audio/ Expanded HDMI v1.3a Ports/ Dual Remotes/ Black Finish', 1699.99); +INSERT INTO Product( title, description, price) VALUES ( 'Denon X-Space Surround Bar Home Theatre System In Black - DHTFS3', 'Denon X-Space Surround Bar Home Theatre System In Black - DHTFS3/ 22 Watts x 5 (6 Ohms)/ Supports All Audio Formats (Dolby Digital, DTS, Dolby Pro Logic II)/ Slim Design/ Includes Dolby Headphones/ Included Subwoofer Features One-Touch Connection/ Remote Control/ Black Finish', 1199.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple Wireless Mighty Mouse - MB111LLA', 'Apple Wireless Mighty Mouse - MB111LLA/ Bluetooth Technology/ Laser Tracking Engine/ 360-Degree Innovative Scroll Ball And Button/ Touch-Sensitive Top Shell/ Force-Sensing Side Buttons/ Customizable/ White Finish', 69.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony PSP 2000 Playstation Portable Gaming System Core 98510 In Piano Black - 711719851004', 'Sony PSP 2000 Playstation Portable Gaming System Core 98510 In Piano Black - 711719851004/ Play Games, Watch Movies On UMD Discs, And Listen To Music All In One Device/ 4.3'' LCD Screen (16:9)/ 480 x 272 Pixel/ Wi-Fi (IEEE 802.11b) For Wireless Networking With Other PSP Owners/ Memory Stick PRO Duo/ 333MHz System Clock Frequency/ Piano Black Finish', 185.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple Mini-DVI To DVI Adapter - M9321GB', 'Apple Mini-DVI To DVI Adapter - M9321GB/ Compatible With iMac (Intel Core Duo), MacBook And 12'' PowerBook G4/ Connects To An External DVI Monitor Or Projector/ White Finish', 19.00); +INSERT INTO Product( title, description, price) VALUES ( 'Olympus DS40 Digital Voice Recorder - DS40R', 'Olympus DS40 Digital Voice Recorder - DS40R/ 136 Hours 15 Minutes Recording Time In LP Mode/ High-Sensitivity Detachable Stereo Microphone/ Voice Guidance/ Three Modes Of Microphone Sensitivity/ Built-In Variable Control Voice Actuator (VCVA) Function/ Up To 32 Hours Of Continuous Operation', 149.00); +INSERT INTO Product( title, description, price) VALUES ( 'Bose Lifestyle 48 Series IV 43479 Home Entertainment System - LS48IVWH', 'Bose Lifestyle 48 Series IV Home Entertainment System - LS48IVWH/ ADAPTiQ Audio Calibration System/ Acoustimass Module/ Jewel Cube Speakers/ Direct/Reflecting Speaker Technology/ VS-2 Video Enhancer/ Proprietary Videostage 5 Decoding Circuitry/ Digital 5.1 Decoding/ Control Integration/ White Finish', 3999.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Black DVD Recorder And VHS Combo Player - RDRVXD655', 'Sony Black DVD Recorder And VHS Combo Player - RDRVXD655/ Multiformat DVD Compatible/ HDMI Output And 720p/1080i Upscaling/ HDTV Tuner/ 4 Video Head Stereo VHS With 19 Micron Heads/ Variable Bit Rate Recording (7 Speeds)/ Progressive Scan Playback/ Dolby Digital And DTS Decoding Playback Compatible/ TV Virtual Surround/ Black Finish', 319.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple 1GB Silver iPod Shuffle - MB225LLA', 'Apple 1GB Silver 2nd Generation iPod Shuffle - MB225LLA/ Holds Up To 240 Songs/ 12 Hours Of Continuous Playback/ Skip-Free Playback/ Battery Indicator/ Shuffle Switch/ Built-In Clip/ Mac And Windows Compatible/ MB226LLA/ Silver Finish', 49.00); +INSERT INTO Product( title, description, price) VALUES ( 'GE 24'' GSD2400NWW White Built-In Dishwasher - GSD2400WH', 'GE 24'' GSD2400NWW White Built-In Dishwasher - GSD2400WH/ 4-Level PowerScrub Wash System/ HotStart Option/ Piranha Hard Food Disposer/ Heated Dry Option/ Self-Cleaning Filter System/ White Finish', 259.00); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic Expandable Digital Cordless DECT 6.0 Phone System - KXTG1032S', 'Panasonic Expandable Digital Cordless DECT 6.0 Phone System - KXTG1032S/ Up To 17 Hours Of Talk Time/ Expandable Up To 6 Handsets/ Up To 3-Way Conference Capability/ Light-Up Indicator With Ringer/Message Alert/ Backlit LCD On Handset/ Digital Speakerphone/ 16-Minute All-Digital Answering System/ Silver Finish', 69.00); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic 2 Line Integrated Corded Phone System - KXTSC14B', 'Panasonic 2 Line Integrated Corded Phone System - KXTSC14B/ 3-Line LCD Display/ Automatic Line Selection/ Call Waiting Caller ID/ 3-Way Conferencing/ Speakerphone/ Navigator Key/ Black Finish', 74.00); +INSERT INTO Product( title, description, price) VALUES ( 'Audiovox XpressEZ XM Satellite Radio Receiver - XMCK5P', 'Audiovox XpressEZ XM Satellite Radio Receiver - XMCK5P/ 10 Programmable Channels/ Universal Connector/ 3-Line Screen Display/ Plug & Play Dock/ Black Finish', 69.99); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic Integrated White Telephone System With All-Digital Answering System - KXTS620W', 'Panasonic Integrated White Telephone System With All-Digital Answering System - KXTS620W/ 2-Way Recording/ Call Waiting Caller ID/ Speakerphone/ 3-Line LCD/ Data Port/ Ringer Indicator Lamp/ Programmable Tone/Pulse/ Wall Mountable/ White Finish', 69.00); +INSERT INTO Product( title, description, price) VALUES ( 'Tech Craft Veneto Series Black TV Stand - ABS60BK', 'Tech Craft Veneto Series Black TV Stand - ABS60BK/ Supports Up To A 60'' Flat Panel TV/ Molded Top Gives It An Elegant Appearance/ Framed Doors To Conceal Components/ 200 Lbs Top Shelf Capacity/ Black Finish', 399.00); +INSERT INTO Product( title, description, price) VALUES ( 'Skagen Premium Steel Slimline Mesh Womens Watch - 233XSGG', 'Skagen Premium Steel Slimline Mesh Womens Watch - 233XSGG/ Stainless Steel Mesh Band/ Elegant Round Case/ Mother-Of-Pearl Dial/ Chrome Indicators', 110.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Color Image Silver Scanner - 8800F', 'Canon Color Image Silver Scanner - 8800F/ Resolution Of Up To 4800 x 9600 Color Dpi/ Enhance Old Photos/ Auto-Image Fix/ USB 2.0 Interface/ Multi-Image Scanning/ Windows And Mac Compatible/ Silver Finish', 199.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Black LocationFree Base Station - LFV30', 'Sony Black LocationFree Base Station - LFV30/ Watch TV Wherever You Are/ Compatible With PC Or PSP System/ Programmable On-Screen Home Remote/ Stream Outside Your Home/ User-Friendly/ Black Finish', 199.00); +INSERT INTO Product( title, description, price) VALUES ( 'Linksys Wireless-G Broadband Router - WRT54GL', 'Linksys Wireless-G Broadband Router - WRT54GL/ All-In-One Internet-Sharing Router/ 4-Port Switch/ Push Button Setup Feature/ TKIP And AES Encryption/ Wireless MAC Address Filtering/ Powerful SPI Firewall/ 54Mbps Wireless-G (802.11g) Access Point', 79.00); +INSERT INTO Product( title, description, price) VALUES ( 'Universal RF Base Station - MRF260', 'Universal RF Base Station - MRF260/ Extends The Reception Range Up To 100 Feet Away Through Walls, Floors, Doors, Indoors Or Outdoors/ Programmed To Operate Equipment At Up To 15 Locations/ Black Finish', 149.00); +INSERT INTO Product( title, description, price) VALUES ( 'Chestnut Hill Sound George iPod Music System In White - CHS4001', 'Chestnut Hill Sound George iPod Music System In White - CHS4001/ Playback System For The iPod/ Full Feature Wireless Remote/ Charging Stand Kit/ Bandless AM/FM Radio/ Easy Set Alarm/ Detachable Front Panel/ White Finish/ iPod Sold Separately', 499.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Universal Remote Control - RMEZ4', 'Sony Universal Remote Control - RMEZ4/ Easy-To-Use Simplified Functions/ Controls TV And Cable Box/ Compatible With Most Of Major Brands/ Large Buttons For Easy Operation/ 3-Minute Memory Backup/ Comfortable Ergonomic Design/ Silver Finish', 16.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sirius SiriusConnect Home Tuner - SCH1', 'Sirius SiriusConnect Home Tuner - SCH1/ Compact Size/ Analog And Digital Audio Outputs/ Display Indicators/ Connects To Any Sirius-Ready Home Audio Component', 49.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus 15'' - 32'' Flat Panel TV Black Wall Mount - MF209B1', 'Sanus 15'' - 32'' Flat Panel TV Black Wall Mount - MF209B1 - MF209B1/ Supports Up To 60 lbs/ Easy To Install/ Virtual Axis 3D Tilting System/ Black Finish', 96.00); +INSERT INTO Product( title, description, price) VALUES ( 'Yamaha High Performance Subwoofer In Black - YSTFSW150BK', 'Yamaha High Performance Subwoofer In Black - YSTFSW150BK/ 130 Watts Dynamic Power/ Advanced YST II (Yamaha Active Servo Technology)/ Linear Port/ Powerful 6.5? Multi-Range Driver/ Magnetically Shielded/ 30 -150 Hz Low Frequency Response/ Down-Firing Active Design/ Best Matching For Front Surround Systems And Micro Component Systems/ Black Finish', 279.00); +INSERT INTO Product( title, description, price) VALUES ( 'Kenwood Sirius Radio Translator For In-Dash Head Units - KCASR50', 'Kenwood Sirius Radio Translator For In-Dash Head Units - KCASR50/ Provides Full Control Of The SIRIUS Radio/ Displays Text On The Stereo And Supplies Power To The Satellite Radio', 60.00); +INSERT INTO Product( title, description, price) VALUES ( 'Linksys Wireless-G PrintServer - WPSM54G', 'Linksys Wireless-G PrintServer - WPSM54G/ Share A Multifunction Printer With Everyone On Your Network (Works With Most USB Printers)/ Allows Full Access To Printing, Faxing, Scanning And Copying Functions/ Connects Your Printer Directly To The Network By 10/100 Wired Ethernet Or 54Mbps Wireless-G', 99.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sirius STILETTO 2 Home Docking Kit - SLH2', 'Sirius STILETTO 2 Home Docking Kit - SLH2/ Stereo Audio Output Connects With Home Audio System Or Speakers/ Headphone Jack/ PC Sync/ Compatible With Stiletto 2 Radios/ Charges Stiletto 2 While Docked/ Black Finish', 49.00); +INSERT INTO Product( title, description, price) VALUES ( 'Microsoft Office Standard 2007 - 02107746', 'Microsoft Office Standard 2007 - 02107746/ Create Documents Faster, More Easily And More Intuitively/ Automatic Document Recovery Tool/ Document Inspector/ Online Tutorials With Step-By-Step Instructions/ Compatible With Windows Vista, Windows XP SP2 And Windows Server 2003 SP1', 399.00); +INSERT INTO Product( title, description, price) VALUES ( 'Jabra Black Bluetooth Headset - BT5010', 'Jabra Black Bluetooth Headset - BT5010/ Up To 10 Hours Of Talk Time And 300 Hours Of Stand-By/ Wind-Noise Reduction Technology/ Vibrating And Visual Alerts/ Colored LED Indicator/ Up To 33-Foot Wireless Range/ Black Finish', 79.00); +INSERT INTO Product( title, description, price) VALUES ( 'Kensington Orbit Optical Trackball Mouse - 64327', 'Kensington Orbit Optical Trackball Mouse - 64327/ DiamondEye Optical Technology For Precise Tracking And Cursor Control/ USB And PS/2 Connectivity For Greater Compatibility/ Windows And Mac Compatible/ Metallic Silver And Black Finish', 38.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sirius Stiletto 2 Vehicle Kit - SLV2', 'Sirius Stiletto 2 Vehicle Kit - SLV2/ Designed For Use With The Sirius Stiletto 2/ Get Direct One-Touch Access To Traffic And Weather/ Built-In FM Transmitter To Play Satellite Radio Through Your Cars Audio System', 49.00); +INSERT INTO Product( title, description, price) VALUES ( 'Denon Networked Audio System With Built-In iPod Dock - S32', 'Denon Networked Audio System With Built-In iPod Dock - S32/ Stream Music Wirelessly/ Buit-In Dock For iPod On Top/ Ability To Decode MP3 And WMA Formats/ High Contrast Graphic LCD/ Multi-Task Jog Wheel On Top/ Built-In Speakers/ Clock With 2 Alarms With Auto Clock Set & Adjust Via Internet/ FM/AM Radio/ WiFi Certified/ Remote Control/ Black Finish', 499.00); +INSERT INTO Product( title, description, price) VALUES ( 'Motorola Portable Bluetooth Car Kit Speaker Phone - T305', 'Motorola Portable Bluetooth Car Kit Speaker Phone - T305/ 2.0 Bluetooth Wireless Technology/ Noise Cancellation Technology/ Loud Sound High Speaker Output/ Convenient Multi-Function Button/ Up To 14 Hours Of Talk Time And 14 Days Of Standby Time/ Mini-USB Connector/ Black Finish', 69.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony HD-Handycam 3 Meters (10 Feet) HDMI Mini Cable - VMC30MHD', 'Sony HD-Handycam 3 Meters (10 Feet) HDMI Mini Cable - VMC30MHD/ Gold Plating Plug/ HDMI-Compliant High Speed Category 2 Cable/ Support Full HD 1080p/ Digital Audio Transfer/ 3 Meters(10 Feet)/ Black Finish', 69.00); +INSERT INTO Product( title, description, price) VALUES ( 'Klipsch Black Wireless iPod Speaker - ROOMGROOVE', 'Klipsch Black Wireless iPod Speaker - ROOMGROOVE/ Wirelessly Sends CD-Quality Music To/From Other RoomGrooves/ Retractable Dock Charges iPods With 30-Pin Connectors/ Horn-Loaded Technology For Precise High Tones/ Dual High-Output Woofers Deliver Room-Filling Bass/ Black Finish', 299.00); +INSERT INTO Product( title, description, price) VALUES ( 'Logitech QuickCam Communicate STX - 961464', 'Logitech QuickCam Communicate STX - 961464/ 640 x 480 Video Capture/ 1.3 Megapixel Still Image Capture/ Built-In Microphone With RightSound Technology/ 30 Frames Per Second/ Adjustable Base Fits Any Monitor Or Notebook/ USB 2.0 Certified', 49.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sirius Plug And Play Universal Home Kit - SUPH1', 'Sirius Plug And Play Universal Home Kit - SUPH1/ Compatible With All Of The Newest SIRIUS Plug & Play Radios/ Stereo Audio Output For Use Your Home Audio System Or Powered Speakers/ Sleek Compact Design/ High Gloss Black Finish', 49.00); +INSERT INTO Product( title, description, price) VALUES ( 'Belkin Hi-Speed USB 2.0 7-Port Hub - F5U237APLS', 'Belkin Hi-Speed USB 2.0 7-Port Hub - F5U237APLS/ Transfers Data Up To 480Mbps/ Installs Easily With Plug-And-Play Convenience/ Works Seamlessly With All USB 1.1 And USB 2.0 Devices/ Over-Current Detection And Safety/ Mac And PC Compatible/ Silver Finish', 49.00); +INSERT INTO Product( title, description, price) VALUES ( 'Netgear Wireless Access Point - WG102', 'Netgear Wireless Access Point - WG102/ High-Speed IEEE 802.11g, Up To 108 Mbps In Turbo Mode/ Wi-Fi Protected Access (WPA 802.11i-Ready Security)/ Integrated IEEE 802.3af Power Over Ethernet (PoE)/ Block SSID Broadcast/ VPN Pass-Through Support', 186.00); +INSERT INTO Product( title, description, price) VALUES ( 'Logitech diNovo Media Desktop Laser Keyboard And Mouse Combo - 967562', 'Logitech diNovo Media Desktop Laser Keyboard And Mouse Combo - 967562/ Unique Ultra-Flat Keyboard/ Detached Customizable MediaPad/ Precision Rechargeable Laser Mouse/ Tilt Wheel Plus Zoom/ Customizable Hotkeys/ Dedicated Search Buttons/ Bluetooth Wireless Technology Compatible/ Black And Gray Finish', 199.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple 500GB Time Capsule Wireless Hard Drive - MB276LLA', 'Apple 500GB Time Capsule Wireless Hard Drive - MB276LLA/ 500GB 7200-rpm Serial ATA Server-Grade Hard Disk Drive/ Up To 5x The Performance And 2x The Range With 802.11n/ Wi-Fi Protected Access (WPA/WPA2)/ Wireless Security (WEP) Configurable For 40-Bit And 128-Bit Encryption/ NAT Firewall/ AirPort Utility For Mac And Windows', 299.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple 1TB Time Capsule Wireless Hard Drive - MB277LLA', 'Apple 1TB Time Capsule Wireless Hard Drive - MB277LLA/ 1TB 7200-rpm Serial ATA Server-Grade Hard Disk Drive/ Up To 5x The Performance And 2x The Range With 802.11n/ Wi-Fi Protected Access (WPA/WPA2)/ Wireless Security (WEP) Configurable For 40-Bit And 128-Bit Encryption/ NAT Firewall/ AirPort Utility For Mac And Windows', 499.00); +INSERT INTO Product( title, description, price) VALUES ( 'Garmin Nuvi Portable Friction Mount - 0101090800', 'Garmin Nuvi Portable Friction Mount - 0101090800/ No Installation Required/ Securely Mounts Your GPS To A Surface In Your Car/ Works With All Garmin Nuvi Portable GPS Units/ Black Finish', 39.00); +INSERT INTO Product( title, description, price) VALUES ( 'Garmin Vehicle Suction Cup Mount - 0101093600', 'Garmin Vehicle Suction Cup Mount - 0101093600/ No Installation Required/ Securely Mounts Your GPS To Dash/ Black Finish', 25.00); +INSERT INTO Product( title, description, price) VALUES ( 'Garmin Suction Cup Mount And 12-Volt Adapter Kit - 0101097900', 'Garmin Suction Cup Mount And 12-Volt Adapter Kit - 0101097900/ Suction Cup Mount And 12-Volt Adapter Included', 30.00); +INSERT INTO Product( title, description, price) VALUES ( 'Pioneer Remote Control With DVD/Audio Controls - CDR55', 'Pioneer Remote Control With DVD/Audio Controls - CDR55/ Hands-Free Wireless Control/ Quick Access For Functions/ Compatible With AVH-P5000DVD, AVH-P4000DVD, AVIC-N4, AVIC-D3, AVH-P4900DVD And AVH-P5900DVD', 19.00); +INSERT INTO Product( title, description, price) VALUES ( 'Pioneer Sirius Bus Interface - CDSB10', 'Pioneer Sirius Bus Interface - CDSB10/ Sirius Connect'' Compatibility/ Replay Function/ Game Alert/ Game Zone', 68.00); +INSERT INTO Product( title, description, price) VALUES ( 'Pioneer HD Radio Tuner - GEXP10HD', 'Pioneer HD Radio Tuner - GEXP10HD/ Compatible With FH-P800BT, FH-P8000BT, DEH-P700BT, DEH-P7000BT, DEH-P600UB, DEH-P6000UB/ ''External Control'' Capable', 100.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sirius Dock And Play Universal Vehicle Kit - SUPV1', 'Sirius Dock And Play Universal Vehicle Kit - SUPV1/ Stereo Audio Output/ Use With Your Home Audio System Or Powered Speakers/ Compatible With Sportster 5, Sportster 4, Sportster 3, Starmate 4, Starmate 3, Stratus 4, Stratus', 49.00); +INSERT INTO Product( title, description, price) VALUES ( 'Electrolux Harmony Series Canister Vacuum - EL6985B', 'Electrolux Harmony Series Canister Vacuum - EL6985B/ Foot-Operated Switch On The Floor Nozel/ Ergonomic Handle Design/ Electronic Dust Bag Indicator/ HEPA H12 Filter/ Telescoping Wand', 299.99); +INSERT INTO Product( title, description, price) VALUES ( 'Sennheiser Rechargeable Nickel-Metal Hydride Battery - BA151', 'Sennheiser Rechargeable Nickel-Metal Hydride Battery - BA151/ Rechargeable Battery For Wireless Sennheiser Headphones', 20.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Green Photo Ink Cartridge - CLI8G', 'Canon Green Photo Ink Cartridge - CLI8G/ FINE Technology For Exceptional Sharpness & Detail/ Compatible With Canon Pixma Pro9000', 16.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Red Photo Ink Cartridge - CLI8R', 'Canon Red Photo Ink Cartridge - CLI8R/ FINE Technology For Exceptional Sharpness & Detail/ Compatible With Canon Pixma Pro9000', 16.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Black Photo Ink Cartridge - CLI8B', 'Canon Black Photo Ink Cartridge - CLI8B/ FINE Technology For Exceptional Sharpness & Detail/ Compatible With Canon Pixma Printers', 16.00); +INSERT INTO Product( title, description, price) VALUES ( 'Netgear ProSafe 5 Port 10/100 Desktop Switch - FS105', 'Netgear ProSafe 5 Port 10/100 Desktop Switch - FS105/ 5 Auto Speed-Sensing 10/100 UTP Ports/ Embedded Memory', 40.00); +INSERT INTO Product( title, description, price) VALUES ( 'Pioneer Premier In-Dash CD/WMA/MP3/AAC Receiver - DEHP400UB', 'Pioneer Premier In-Dash CD/WMA/MP3/AAC Receiver - DEHP400UB/ 50W x 4 Built-In Speaker Power/ 3 RCA Hi-Volt Preouts/ Two-Way Crossover/ Supertuner IIID/ AUX-In Connection/ Rotary Commander Volume Control', 183.00); +INSERT INTO Product( title, description, price) VALUES ( 'Pioneer DEH-2000MP CD/MP3/WMA In-Dash Receiver - DEH2000MP', 'Pioneer DEH-2000MP CD/MP3/WMA In-Dash Receiver - DEH2000MP/ 50W x 4 Built-In Speaker Power/ LED Display/ Built-In Front Auxiliary Input/ Detachable Face Security/ Remote Control/ Rotary Volume Control', 98.00); +INSERT INTO Product( title, description, price) VALUES ( 'Pioneer Premier In-Dash CD/WMA/MP3/AAC Receiver - DEHP500UB', 'Pioneer Premier In-Dash CD/WMA/MP3/AAC Receiver - DEHP500UB/ 50W x 4 Built-In Speaker Power/ 3 RCA Hi-Volt Preouts/ Two-Way Crossover/ Supertuner IIID/ AUX-In Connection/ Rotary Commander Volume Control', 208.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony 7'' Digital Photo Frame In Black - DPFD70', 'Sony 7'' Digital Photo Frame - DPFD70/ 7'' LCD With 800 x 480 Resolution/ 15:9 Aspect Ratio/ 256MB Internal Memory/ Supports Most Memory Cards/ Variety Of Display Modes/ Auto Image Rotation Feature/ View Mode Button/ Fast Digital Picture Decoding/ Handles Large Data Files/ USB B-Type Connection/ Remote Control/ Black Finish', 139.00); +INSERT INTO Product( title, description, price) VALUES ( 'SIRIUS SiriusConnect Vehicle Kit In Black - SCVDOC1', 'SIRIUS SiriusConnect Vehicle Kit In Black - SCVDOC1/ Compact Design/ Compatible With The Next Generation Of SiriusConnect Interface Adapters/ 3 Interchangeable Adapter Plates/ Black Finish', 59.00); +INSERT INTO Product( title, description, price) VALUES ( 'Linksys Wireless-G Ethernet Bridge - WET54G', 'Linksys Wireless-G Ethernet Bridge - WET54G/ Converts Wired-Ethernet Devices To Wireless-G Network Connectivity/ For Windows, Macintosh, Linux, PlayStation Consoles, Xbox Consoles, Or Anything With An Ethernet Port/ Plug And Play, No Driver Installation Require', 89.00); +INSERT INTO Product( title, description, price) VALUES ( 'Yamaha RX-V363BL 5.1 Channel Digital Home Theater Receiver In Black - RXV363BK', 'Yamaha RX-V363BL 5.1 Channel Digital Home Theater Receiver In Black - RXV363BK/ 500 Watts Powerful Surround Sound (100W x 5)/ iPod And Bluetooth Audio Compatibility/ Dolby Digital, DTS And Dolby Pro Logic II Decoding/ Cinema DSP With 8 DSP Programs/ Compressed Music Enhancer/ 192kHz/24-Bit DACs For All Channels/ Remote Control/ Black Finish', 199.00); +INSERT INTO Product( title, description, price) VALUES ( 'Yamaha All-Weather Pair Speaker System - NSAW390WH', 'Yamaha NS-AW390WH All-Weather Speaker System - NSAW390WH/ 6.5'' High Compliance/ PP Mica Filled Woofers/ Unique Dual 1'' PEI Dome Tweeter Configuration/ Aluminum Speaker Grilles/ Wall Mountable/ White Finish/ Sold As A Pair', 149.00); +INSERT INTO Product( title, description, price) VALUES ( 'Yamaha NS-AW390BL All-Weather Pair Speaker System - NSAW390BK', 'Yamaha NS-AW390BL All-Weather Speaker System - NSAW390BK/ 6.5'' High Compliance/ PP Mica Filled Woofers/ Unique Dual 1'' PEI Dome Tweeter Configuration/ Aluminum Speaker Grilles/ Wall Mountable/ Black Finish/ Sold As A Pair', 149.00); +INSERT INTO Product( title, description, price) VALUES ( 'Yamaha NS-AW190BL All-Weather Pair Speaker System - NSAW190BK', 'Yamaha NS-AW190BL All-Weather Speaker System - NSAW190BK/ 5'' High Compliance/ PP Mica Filled Woofers/ Unique Dual 1/2'' PEI Dome Tweeter Configuration/ Aluminum Speaker Grilles/ Wall Mountable/ Black Finish/ Sold As A Pair', 99.00); +INSERT INTO Product( title, description, price) VALUES ( 'Yamaha 7.2 Channel Black Digital Home Theater Receiver - RXV663BK', 'Yamaha 7.2 Channel Black Digital Home Theater Receiver - RXV663BK/ 4 SCENE Buttons/ XM Ready With XM HD Surround/ SIRIUS Satellite Radio Ready/ YPAO/ iPod Compatibility/ Bluetooth Compatibility/ Multi-Zone Control Compatibility/ On-Screen Display/ Black Finish', 499.00); +INSERT INTO Product( title, description, price) VALUES ( 'Yamaha 7.2 Channel Black Digital Home Theater Receiver - RXV863BK', 'Yamaha 7.2 Channel Black Digital Home Theater Receiver - RXV863BK/ 4 SCENE Buttons/ XM Ready With XM HD Surround/ SIRIUS Satellite Radio Ready/ YPAO/ iPod Compatibility/ Bluetooth Compatibility/ Multi-Zone Control Compatibility/ On-Screen Display/ Black Finish/ CALL FOR LATEST PRICE', 899.00); +INSERT INTO Product( title, description, price) VALUES ( 'Yamaha 5.1 Channel Home Theater In A Box System In Black - YHT390BK', 'Yamaha 5.1 Channel Home Theater In A Box System In Black - YHT390BK/ New Scene, Compressed Music Enhancer/ iPod Compatibility/ 600W Powerful Surround Sound/ Silent Cinema/ 192kHz/24-Bit DACs For All Channels/ Black Finish', 399.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple MacBook Air SuperDrive - MB397GA', 'Apple MacBook Air SuperDrive - MB397GA/ Compact And Portable Slot-Loading 8x SuperDrive/ Connect To MacBook Air USB Slot/ Play And Burn Both CDs And DVDs/ Watch DVD Movie, Install Software, Or Create Backup Discs', 99.00); +INSERT INTO Product( title, description, price) VALUES ( 'Monster Mini-To-Mini iCable For Car - AICMINIIP3S', 'Monster Mini-To-Mini iCable For Car - AICMINIIP3S/ 3 Ft. Cable/ 1/8 Mini-Jack Input For Car Stereo/ Dual Balanced High-Purity Copper Conductors And 24k Gold/ Xtra Low Noise DoubleHelix Construction/ Compatible With Apple iPod, iPhone, And Portable MP3 Player/ White Finish', 15.00); +INSERT INTO Product( title, description, price) VALUES ( 'TomTom GPS Mount And USB Car Charger - 9N00101', 'TomTom GPS Mount And USB Car Charger - 9N00101/ Extra Holder And Car Charger Convenient For Multi-Vehicles User/ No Need To Transfer Holder From One Car To Another/ Easy Installation/ Compatible With TomTom ONE/ Black Finish', 39.00); +INSERT INTO Product( title, description, price) VALUES ( 'Denon Blu-ray Disc DVD/CD Player - DVD3800BDCI', 'Denon Blu-ray Disc DVD/CD Player - DVD3800BDCI/ 10-Bit Realta HQV Video Processor/ 1080p/24fps Output And Multi-Cadence Detection/ HDMI 1.3a Output With 36-Bit Deep Color Support/ Dual 32-Bit Floating Point DSP/ Multi-Layered Construction With Dual-Layered Top Shields And Triple-Layered Bottom/ Suppress Vibration Hybrid (S.V.H.) Loader/ Black Finish', 1999.00); +INSERT INTO Product( title, description, price) VALUES ( 'TomTom GPS Mount And USB Car Charger - 9S00006', 'TomTom GPS Mount And USB Car Charger - 9S00006/ Extra Holder And Car Charger Convenient For Multi-Vehicles User/ No Need To Transfer Holder From One Car To Another/ Easy Installation/ Compatible With TomTom ONE XL/ Black Finish', 28.00); +INSERT INTO Product( title, description, price) VALUES ( 'Bracketron iPod Docking Kit - IPM202BL', 'Bracketron iPod Docking Kit - IPM202BL/ Compatible With All Generation iPods Including iPod Mini, iPod Nano And Apple iPhone/ Wings Adjustable Up To 2.5''/ Black Finish', 14.95); +INSERT INTO Product( title, description, price) VALUES ( 'Boston Acoustics Solo AM/FM Large Display Clock Radio - HSOLOMDNT', 'Boston Acoustics Solo AM/FM Large Display Clock Radio - HSOLOMDNT/ Rotating Clock Face/ Precision Tuner/ 3 1/2? Full-Range Speaker/ Auxiliary Input/ High Contrast LCD Display/ 360� Snooze Bar/ Grey Finish', 99.00); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic 5.8 GHz Black Expandable Digital Cordless Phone System - KXTG4323B', 'Panasonic 5.8 GHz Black Expandable Digital Cordless Phone System - KXTG4323B/ Include 3 Handsets/ Expandable Up To 4 Handsets/ Digital Answering Machine System/ Ringer ID/ Call Waiting Caller ID/ Voicemail/ Hold/ Mute/ Clock/ Alarm/ LED Lighting/ Speakerphone/ Intercom/ 11 Days Standby/ 5 Hours Talk Time/ Black Finish', 79.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Silver Digital Voice Recorder - ICDB600', 'Sony Silver Digital Voice Recorder - ICDB600/ 512MB Built-In Flash Memory/ Up To 300 Hours Of Recording Time/ 3 Recording Modes/ 4 Message Folders/ Large LCD Display/ Voice Operated Recording/ 250mW Speaker Output/ Date and Time Stamp/ Silver Finish', 39.00); +INSERT INTO Product( title, description, price) VALUES ( 'Motorola MotoRokr Portable Bluetooth Car Kit Speaker Phone - T505', 'Motorola MotoRokr Portable Bluetooth Car Kit Speaker Phone - T505/ 2.0 Bluetooth Wireless Technology/ Noise Cancellation Technology/ Loud Sound High Speaker Output/ Audio CallerID/ StationFinder/ Convenient Multi-Function Button/ Long Battery Life/ Mini-USB Connector/ Black Finish', 129.00); +INSERT INTO Product( title, description, price) VALUES ( 'Samsung Hi Definition Conversion DVD Player - DVD1080P8', 'Samsung Hi Definition Conversion DVD Player - DVD1080P8/ Progressive Scan/ HD Upconversion/ Digital-To-Analog Converter/ Dolby Digital Surround Sound/ Child Protection/ HDMI Output/ Black Finish', 79.90); +INSERT INTO Product( title, description, price) VALUES ( 'Boston Acoustics Duo-I AM/FM Clock Radio With iPod Dock - HDUOIMDNT', 'Boston Acoustics Duo-I AM/FM Clock Radio With iPod Dock - HDUOIMDNT/ Integrated iPod Dock/ Precision AM/FM Stereo Tuner/ 3 1/2'' Dual High Performance Full-Range Speakers/ BassTrac Audio Processing/ 2 Auxiliary Inputs/ High Contrast Display/ 10 FM & 5 AM Station Presets/ 360� Snooze Bar/ Remote Control Included/ Grey Finish', 199.00); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic Black DVD Home Theater Sound System - SCPT660', 'Panasonic Black DVD Home Theater Sound System - SCPT660/ Kelton Subwoofer/ Bamboo Diaphragm Center Speakers/ 1080p Up-Conversion/ Integrated Universal Dock And On-Screen Display For iPod/ iPod Video Playback/ Whisper-Mode Surround/ Built-In Dolby Digital And DTS Decoder/ High Speed 5-DVD/CD Changer/ Black Finish', 289.00); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Summit E-620 Copper Liquid Propane Gas Outdoor Grill - 1752001', 'Weber Summit E-620 Copper Liquid Propane Gas Outdoor Grill - 1752001/ 6 Stainless Steel Burners/ 60,000 BTU-Per-Hour Input/ Snap-Jet Individual Burner Ignition System/ 838 Sq. In. Total Cooking Area/ Porcelain-Enameled Shroud/ Center-Mounted Thermometer/ Copper Finish/ Liquid Propane Model (LP Tank Not Included)/ Assembly Required', 1899.00); +INSERT INTO Product( title, description, price) VALUES ( 'Logitech Harmony One Advanced Universal Remote Control - HARMONY1', 'Logitech Harmony One Advanced Universal Remote Control - HARMONY1/ One-Touch Access To Your Entertainment/ Replaces Up To 15 Remotes/ Full-Color Touch Screen/ Sculpted, Backlighted Buttons In Logical Zones/ Ergonomic Design/ Rechargeable/ Guided Online Setup/ World?s Largest AV Control Database/915-000035', 249.00); +INSERT INTO Product( title, description, price) VALUES ( 'Z-Line Portland Black TV Stand - ZL2344MU', 'Z-Line Portland Black TV Stand - ZL2344MU/ Accommodates Most Flat Panel LCD/Plasma TVs Up To 50''/ Durable Black Glossy Powder Coat Metal Frame/ Tempered Black Safety Glass Shelves/ Chrome Cylinder Glass Supports/ Swivel Mount/ Wire Management/ Holds 65 lbs Per Shelf/ Distance Between Shelves- 8.25'' (Bottom to Middle) and 7'' (Middle to the bar below the top shelf)/ Black Finish', 399.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Black USB Stereo Turntable System - PSLX300USB', 'Sony Black USB Stereo Turntable System - PSLX300USB/ Transfer Classic Vinyl To PC, Walkman, Or MP3 Player/ USB And RCA Audio Output/ 45 Rpm Record Playback/ Belt Drive System/ Dust Cover/ Black Finish', 149.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Digital SLR Camera With Lens Kit - DSLRA200W', 'Sony Alpha Digital SLR Camera With Lens Kit - DSLRA200W/ 10.2 Megapixels/ 2.7'' Clear Photo LCD Screen/ Super SteadyShot In-Camera Image Stabilization/ Continuous Burst Mode/ Bionz Image Processor/ ISO Sensitivity/ 9-Point Center Cross AF Sensor/ Scene Selection Modes/ DT 18-70mm f3.5 Zoom Lens And 75-300mm f4.5-5.6 Compact Super Telephoto Zoom Lens Included/ Black Finish', 849.99); +INSERT INTO Product( title, description, price) VALUES ( 'Nyko Charge Base 2 Charger For PlayStation 3 Controller - 743840830535', 'Nyko Charge Base 2 Charger For PlayStation 3 Controller - 743840830535/ Compact Design/ Rapidly Charges Two PS3 Controllers Simultaneously/ Includes Two USB Charge Adaptors', 34.99); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Remote Control Tripod - VCT60AV', 'Sony Remote Control Tripod - VCT60AV/ A/V Remote Connector/ 4 Stage Leg Extension/ Extends From 18.9'' To 57.6'' In Height/ Guide Frame Feature/ Available Vertical Position/ Supplied Carrying Case/ Black Finish', 99.00); +INSERT INTO Product( title, description, price) VALUES ( 'Klipsch Groove PM20 Computer Speakers - GROOVEPM20BK', 'Klipsch Groove PM20 Computer Speakers - GROOVEPM20BK/ 6 Ohms Nominal Impedance/ 3Full-Range 2.5? Fiber Composite Driver/ Overload Protection/ System-Specific Loudness Contour/ Black Finish', 99.00); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Gas Barbecue Rotisserie - 7519', 'Weber Gas Barbecue Rotisserie - 7519/ Fits Genesis E-300, S-300 Gas Grills/ Heavy-Duty Electric Motor/ Counterbalance For Smooth Turning', 80.00); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Cast Iron Griddle - 7531', 'Weber Cast Iron Griddle - 7531/ Heavy-Duty Cast Iron Griddle/ Fits Weber Genesis Silver A & Spirit 500 Gas Grills', 44.99); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Cast Iron Griddle - 7542', 'Weber Cast Iron Griddle - 7542/ Heavy-Duty Cast Iron Griddle/ Two-Sided For Cooking A Variety Of Foods/ Fits Several Weber Grills', 44.99); +INSERT INTO Product( title, description, price) VALUES ( 'Sony 5.1 Channel Black A/V Receiver - STRDG520', 'Sony 5.1 Channel Black A/V Receiver - STRDG520/ 100 Watts X 5 Power/ 1080p HDMI Pass Through/ Accepts 1080/60p And 24p Video Signal Via HDMI/ Dolby Digital, Dolby Pro Logic, Dolby Pro Logic II, Digital Cinema Sound And Dts Decoding/ Black Finish', 199.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Alpha DSLR Black Camera Body With 18-70mm Zoom Lens - DSLRA300K', 'Sony Alpha DSLR Black Camera Body With 18-70mm Zoom Lens - DSLRA300K/ 10.2 MP Super HAD CCD/ Tiltable 2.7 Clear Photo LCD Plus Screen/ Smart Teleconverter 2X Zoom/ Expanded ISO Sensitivity/ Super SteadyShot In-Camera Image Stabilization/ Anti-Dust Technology/ 9-Point Center Cross AF Sensor/ Index and Slide Show Display/ Black Finish', 599.99); +INSERT INTO Product( title, description, price) VALUES ( 'Polk Audio CSI A4 Black Center Channel Loudspeaker - CSIA4BK', 'Polk Audio CSI A4 Black Center Channel Loudspeaker - CSIA4BK/ 1'' Silk/Polymer Dome Tweeter/ Dual 5-1/4'' Mid/Woofers/ Magnetic Shielding/ All-MDF Construction/ Acoustically Inert Stamped Driver Baskets/ Floating Anti-Diffraction Grilles/ Dual Bi-Ampable Gold-Plated 5-Way Binding Post Inputs/ Black Finish/ Sold As A Single', 279.95); +INSERT INTO Product( title, description, price) VALUES ( 'Polk Audio CSI A4 Cherry Center Channel Loudspeaker - CSIA4CH', 'Polk Audio CSI A4 Cherry Center Channel Loudspeaker - CSIA4CH/ 1'' Silk/Polymer Dome Tweeter/ Dual 5-1/4'' Mid/Woofers/ Magnetic Shielding/ All-MDF Construction/ Acoustically Inert Stamped Driver Baskets/ Floating Anti-Diffraction Grilles/ Dual Bi-Ampable Gold-Plated 5-Way Binding Post Inputs/ Cherry Finish/ Sold As A Single', 279.95); +INSERT INTO Product( title, description, price) VALUES ( 'Polk Audio CSI A6 Black Center Channel Loudspeaker - CSIA6BK', 'Polk Audio CSI A6 Black Center Channel Loudspeaker - CSIA6BK/ Dual 6-1/2'' Mid/Woofers/ 1'' Silk/Polymer Dome Tweeter/ Dual Rear PowerPort Bass Venting/ Magnetic Shielding/ Mylar Bypass Capacitors/ Acoustic Resonance Control (ARC Port) Technology/ Dual Bi-Ampable Gold-Plated 5-Way Binding Post Inputs/ Butyl Rubber Surrounds/ Floating Anti-Diffraction Grilles/ All-MDF Construction/ Black Finish/ Sold As A Single', 449.95); +INSERT INTO Product( title, description, price) VALUES ( 'Polk Audio 5.1 Channel Black Home Theater Speaker System - RM705BK', 'Polk Audio 5.1 Channel Black Home Theater Speaker System - RM705BK/ Heavy-Duty Non-Resonant Composite Enclosures/ Downward Firing Powered 8'' Subwoofer/ Built-In Subwoofer Power Amp With Active Crossover/ Three-Sided Cabinet Design/ Magnetically Shielded/ Black Finish', 499.95); +INSERT INTO Product( title, description, price) VALUES ( 'Sony 3.1 Channel Home Theater Surround System In Black - HTCT100', 'Sony 3.1 Channel Home Theater Surround System In Black - HTCT100/ HDMI Active Intelligence/ LPCM Playback/ 3.1 Channel/ S-Force Surround/ BRAVIA� Sync/ Digital Media Port/ Black Finish/ ETA MID JANUARY 2009', 299.00); +INSERT INTO Product( title, description, price) VALUES ( 'Polk Audio Black 10'' Powered Subwoofer - PSW110BK', 'Polk Audio PSW110 Black 10'' Powered Subwoofer - PSW110BK/ 100 Watts Continuous Average Output/ 200 Watts Dynamic Power Output/ 10'' Dynamic Balance Composite Woofer Driver/ Klippel Optimized Woofer/ High Current Class A/B Amplifiers/ Downward Firing Port/ Line And Speaker Level Inputs/ Non-Resonant MDF Construction/ Black Finish', 299.95); +INSERT INTO Product( title, description, price) VALUES ( 'Twenty20 VholdR Mount Adhesive - 2200MA', 'Twenty20 VholdR Mount Adhesive - 2200MA/ Removable/ Resists Water, Heat And Cold', 6.00); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Premium Black Grill Cover - 7550', 'Weber Premium Black Grill Cover - 7550/ Heavy-Duty Vinyl/ Compatible With Spirit E-200 Series, Spirit 500 And Genesis Silver A Gas Grills/ Black Finish', 40.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Black Earbud Style Headphones - MDREX55BK', 'Sony MDREX55BLK Black Earbud Style Headphones - MDREX55BK/ 9mm EX Driver Provides Comfort Fit And Deep Bass Sound/ Soft Fitting Silicon Housing/ 3 Sizes Earbuds/ Carrying Pouch/ Black Finish', 39.00); +INSERT INTO Product( title, description, price) VALUES ( 'Hoover EmPower Bagless Upright Vacuum - U5269', 'Hoover EmPower Bagless Upright Vacuum - U5269/ Hush Mode/ Power Boost/ No Assembly Required/ Folding Handle For Easy Storage/ Allergen Filter/ 12 Amp Motor/ Tools Included/ Green Finish', 99.00); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic DECT 6.0 Expandable Digital Cordless Phone With All-Digital Answering System - KXTG9344T', 'Panasonic DECT 6.0 Expandable Digital Cordless Phone With All-Digital Answering System - KXTG9344T/ 4 Handsets System/ Up To 6 Multi-Handset Capability/ Digital Answering Machine System/ Ringer ID/ Call Waiting Caller ID/ Voicemail/ Hold/ Voice Menu/ Marker Message/ Mute/ Clock/ Alarm/ LED Lighting/ Night Mode/ Call Block/ Speakerphone/ 11 Days Standby/ 5 Hours Talk Time/ Black Metallic Finish', 139.00); +INSERT INTO Product( title, description, price) VALUES ( 'LaCie 500GB d2 Quadra External Hard Drive - 301825U', 'LaCie 500GB d2 Quadra External Hard Drive - 301825U/ Quadruple Interface For Full PC And Mac Compatibility/ Interface Bandwidth Up To 3Gbits/s (eSATA)/ Advanced Aluminum Heat Sink Design Cooling System For Quiet Operation/ 7200 Rotational Speed (rpm)/ 16MB Cache/ Compatible With Time Machine', 159.00); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic DECT 6.0 Black Expandable Digital Cordless Phone - KXTG9361B', 'Panasonic DECT 6.0 Black Expandable Digital Cordless Phone - KXTG9361B/ Drop And Splash Resistant/ Multi-Handset Capability/ Ringer ID/ Call Waiting Caller ID/ Voicemail/ Hold/ Mute/ Clock/ Alarm/ LED Lighting/ Night Mode/ Call Block/ Speakerphone/ 11 Days Standby/ 5 Hours Talk Time/ Black Finish', 48.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony 1GB Memory Stick PRO Duo Mark 2 Media Card - MSMT1G', 'Sony 1GB Memory Stick PRO Duo Mark 2 Media Card - MSMT1G/ 160 Mbps Transfer Speed/ 940MB Actual Capacity', 19.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony 2GB Memory Stick PRO Duo Mark 2 Media Card - MSMT2G', 'Sony 2GB Memory Stick PRO Duo Mark 2 Media Card - MSMT2G/ 160 Mbps Transfer Speed/ 1.85GB Actual Capacity', 29.99); +INSERT INTO Product( title, description, price) VALUES ( 'Pioneer Black Premier Single CD Receiver - DEHP700BT', 'Pioneer Black Premier Single CD Receiver - DEHP700BT/ Built-In Bluetooth Solution/ USB Direct Control For iPod/ Advanced Sound Retriever/ 3 RCA Hi-Volt Preouts/ Two-Way Crossover/ Built-In MOSFET 50 W x 4 Amplifier/ Supertuner IIID/ Amplifier Off Mode/ Display Off Mode', 308.00); +INSERT INTO Product( title, description, price) VALUES ( 'TomTom ONE 130S Car GPS Navigation System - 1EE005202', 'TomTom ONE 130S Car GPS Navigation System - 1EE005202/ 3.5'' LCD Anti-Glare Touch Screen/ Pre-Installed Maps/ Internal Lithium-Ion Battery/ Car Speed Linked Volume/ Automatic Day/Night Mode/ QuickGPSfix/ Text-To-Speech Included', 246.95); +INSERT INTO Product( title, description, price) VALUES ( 'TomTom ONE XL 330 Car GPS Navigation System - 1EG005200', 'TomTom ONE XL 330 Car GPS Navigation System - 1EG005200/ 4.3'' LCD Anti-Glare Touch Screen/ Pre-Installed Maps/ Internal Lithium-Ion Battery/ Car Speed Linked Volume/ Automatic Day/Night Mode/ QuickGPSfix', 246.95); +INSERT INTO Product( title, description, price) VALUES ( 'TomTom ONE XL 330S Car GPS Navigation System - 1EG005201', 'TomTom ONE XL 330S Car GPS Navigation System - 1EG005201/ 4.3'' LCD Anti-Glare Touch Screen/ Pre-Installed Maps/ Internal Lithium-Ion Battery/ Car Speed Linked Volume/ Automatic Day/Night Mode/ QuickGPSfix/ Text-To-Speech Included', 296.95); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Black Camcorder Tripod - VCT80AV', 'Sony Black Camcorder Tripod - VCT80AV/ 3 Stage Leg Extension/ Extends From 24.88'' To 65.88'' In Height/ Guide Frame Feature/ Available Vertical Position/ For A/V Remote Connector/ Supplied Carrying Case/ Black Finish', 180.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony 7.1 Channel Black A/V Receiver - STRDG820', 'Sony 7.1 Channel Black A/V Receiver - STRDG820/ 770 Watts Total Power (110W x 7)/ Accepting Resolutions Up To 1080p Via HDMI/ Digital Cinema Auto Calibration/ BRAVIA Sync/ Digital Cinema Sound/ Dolby Digital, Dolby Digital EX, Dolby Pro Logic II, Dolby Pro Logic IIx, Dts, Dts-ES, Dts 96/24, Dts NEO:6 Decoding/ Black Finish', 399.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Splash Resistant Shower Radio - ICFS79W', 'Sony Multi Band Digital Tuner Shower Radio - ICFS79W/ Splash Resistant/ AM/FM/Weather Band Reception/ Easy One Button Weather Band Select/ Built-In Digital Clock/ Selectable Automatic-Off Timer/ Quartz Synthesized Tuner/ Built-In Antennas', 49.00); +INSERT INTO Product( title, description, price) VALUES ( 'Samsung Black Combo DVD/VHS Player - DVDV9800', 'Samsung Black Combo DVD/VHS Player - DVDV9800/ Progressive Scan/ 1080p Up-Conversion Via HDMI/ 10Bit / 54MHz Digital-To-Analog Converter/ 4-Head Hi-Fi VCR/ Black Finish/ No Tuner', 98.00); +INSERT INTO Product( title, description, price) VALUES ( 'Omnimount Stellar Series Audio Tower - G303DARK', 'Omnimount Stellar Series Audio Tower - G303DARK/ Designed For Large Audio Components/ Supports Tabletop Flat Panels/ Three ?Floating? Shelf Design/ Integrated Cable Management/ Black Finish', 299.00); +INSERT INTO Product( title, description, price) VALUES ( 'Weber Q 320 Liquid Propane Table And Outdoor Grill - 586002', 'Weber Q 320 Liquid Propane Table And Outdoor Grill - 586002/ 462 Sq. In. Total Cooking Area/ 21,700 BTU Stainless Steel Burners/ Porcelain-Enameled Cast-Iron Cooking Grate/ Cast-Aluminum Lid And Body/ Electronic Ignition/ Grill Out Handle Light/ Folding Work Tables With Tool Holders/ Removable Catch Pan/ Built-In Thermometer/ Stationary Cart Included/ Black And Aluminum Finish/ Assembly Required', 379.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Black Component Home Theater System - HT7200DH', 'Sony Black Component Home Theater System - HT7200DH/ 900 Watts/ 5.1 Channels/ HDMI 1080p Output DVD Player/ HDMI Active Intellegence AV Receiver/ 5 Satellite Speakers/ XM Ready/ Black Finish', 499.00); +INSERT INTO Product( title, description, price) VALUES ( 'TomTom Black Carry Case - 9UEA01700', 'TomTom Black Carry Case - 9UEA01700/ Specifically Designed For TomTom ONE 130 GPS/ Durable And Compact Protective Hardcover Material/ Wrist Strap/ Black Finish', 19.00); +INSERT INTO Product( title, description, price) VALUES ( 'Mitsubishi 835 Diamond Series 73'' 1080p DLP Rear Projection HDTV - WD73835', 'Mitsubishi 835 Diamond Series 73'' 1080p DLP Rear Projection HDTV - WD73835/ 1920 x 1080 Full HD Resolution/ 6-Color Processor/ Smooth 120Hz/ x.v.Color/ Plush1080p 12-Bit Digital Video Processing/ Color 4D Video Noise Reduction/ 3D Ready/ NetCommand/ DeepField Imager/ Black Finish', 3499.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Black DVD Recorder And VHS Combo Player - RDRVX560', 'Sony Black DVD Recorder And VHS Combo Player - RDRVX560/ Multiformat DVD Compatible/ HDMI Output With 1080p,1080i,720p Upscaling/ USB One Touch Dubbing/ 4 Video Head Stereo VHS With 19 Micron Heads/ Virtual Surround Sound For DVD With Stereo TV Speakers/ Black Finish', 219.00); +INSERT INTO Product( title, description, price) VALUES ( 'Uniden DECT 6.0 Digital Accessory Handset - DCX300', 'Uniden DECT 6.0 Digital Accessory Handset - DCX300/ DECT 6.0 Interference Free Cordless Frequency/ Large Color Display/ Blue Backlit Keypad/ Handset Speakerphone/ Intercom or Call Transfer Between Handsets/ Polyphonic Ring Tones/ Advanced Phonebook Features/ Last 5 Number Redial/ Piano Black Finish', 34.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony DVD Recorder In Black - RDRGX360', 'Sony DVD Recorder In Black - RDRGX360/ 1080p/1080i/720p Upscaling For DVD/ USB One Touch Dubbing/ Line Input Recording/ BRAVIA Sync/ DVD+R Double Layer Recording/ Dolby Digital Decoding Playback Compatible/ Black Finish', 179.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sennheisser Hi-Fi Wireless Headphone - RS130', 'Sennheisser Hi-Fi Wireless Headphone - RS130/ Switchable SRS Surround Sound Mode/ Intelligent Auto-Tuning/ Memory Function/ Self-Learning Automatic Level Control/ Black Finish', 159.95); +INSERT INTO Product( title, description, price) VALUES ( 'BlueAnt Black Bluetooth Headset - Z9I', 'BlueAnt Black Bluetooth Headset - Z9I/ Pairs With 5 Devices/ Bluetooth Version 2.0 Technology/ Dual Microphones For Pure Speech/ Revolutionary Voice Isolation Technology/ Automatic Connection And Reconnection With Notification/ Firmware Upgrade Via USB On Your PC/ Up To 5.5 Hours Talk Time/ 200 Hours Standby Time/ Gloss Black Finish', 99.00); +INSERT INTO Product( title, description, price) VALUES ( 'Escort Passport 9500CI Radar Detector - 9500CI', 'Escort Passport 9500CI Radar Detector - 9500CI/ 360 Degree Protection/ Completely Undetectable To All Detector Scanners/ Variable-Speed Radar Performance/ GPS-Powered Truelock Filter/ Adaptive Signal Processing/ Speed Alert/ Crystal-Clear Voice Alerts/ 280 LED Matrix Blue Display/ 5 Levels Of Brightness Control/ Black Finish/ Price Includes Installation', 1995.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Black 5.1 Channel Home Theater System - HTDDWG700', 'Sony Black 5.1 Channel Home Theater System - HTDDWG700/ 5.1 Channel/ 900 Watts Power/ Portable Audio Enhancer With Front Audio Input/ iPod Cradle/ Digital Cinema Auto Calibration/ BRAVIA Sync/ Digital Media Port/ Black Finish', 199.00); +INSERT INTO Product( title, description, price) VALUES ( 'Denon Multi-Channel Digital Surround Sound Speaker System - DHTFS5', 'Denon Multi-Channel Digital Surround Sound Speaker System - DHTFS5/ Multiple Amplifier Channels/ Dolby Pro Logic II, Dolby Digital And DTS Surround/ Balanced Loudspeaker Drivers/ X-Space Surround Technology/ Night Mode/ Remote Control Included/ Black Finish', 499.00); +INSERT INTO Product( title, description, price) VALUES ( 'Danby White Countertop Dishwasher - DDW497WH', 'Danby White Countertop Dishwasher - DDW497WH/ 4 Place Settings/ Quick Connect To Any Kitchen Tap/ Automatic Detergent And Rinse Agent Dispenser/ Quiet Operation/ 5 Wash Cycles/ Durable Stainless Steel Spray Arm And Interior/ White Finish', 222.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon KP-36IP Color Ink & Paper Set - 7737A001', 'Canon KP-36IP Color Ink & Paper Set - 7737A001/ 36 Sheets Of 4'' x 6'' Photo Paper/ Ink Cartridge For Compatible Canon Dye Sublimation Printer', 12.00); +INSERT INTO Product( title, description, price) VALUES ( 'Linksys Ultra RangePlus Wireless-N Broadband Router - WRT160N', 'Linksys Ultra RangePlus Wireless-N Broadband Router - WRT160N/ Internet-Sharing Router/ 4-Port Switch/ Enhanced Wireless Access Point/ MIMO Technology/ Faster Than Wireless-G/ Protected By Wireless Encryption/ Powerful SPI Firewall/ 2 Antennas/ Glossy Black Finish', 79.00); +INSERT INTO Product( title, description, price) VALUES ( 'Pioneer USB iPod Interface Cable - CDIU230V', 'Pioneer USB iPod Interface Cable - CDIU230V/ Compatible With AVIC-F700BT And AVIC-F900BT Navigation Receivers', 48.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon White Selphy CP760 Compact Photo Printer - 2565B001', 'Canon White Selphy CP760 Compact Photo Printer - 2565B001/ 2.5'' TFT Display/ Portrait Image Optimize/ Print Water-Resistant Photos/ Print Directly From Your Memory Cards Or Bluetooth-Enabled Devices/ Big Buttons/ Automatic Red-Eye Correction/ White Finish', 95.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony 2GB Memory Stick Micro (M2) - MSA2GU2', 'Sony MSA2GD 2GB Memory Stick Micro (M2) - MSA2GU2/ Ultra-Small Size/ 2GB Storage Capacity With 1.85GB Available/ Designed For Use In Compatible Small Devices Such As Mobile Phone/ Dual Operating Voltage/ M2 Duo Adaptor Included/ Black Finish', 29.99); +INSERT INTO Product( title, description, price) VALUES ( 'Panasonic DECT 6.0 Silver Digital Cordless Handset - KXTGA630S', 'Panasonic DECT 6.0 Silver Digital Cordless Handset - KXTGA630S/ For Use With Panasonic 6300 And 9300 Series Phone Systems/ DECT 6.0 Technology/ 60 Channels/ Call Waiting Caller ID/ 1.4'' Amber Backlit LCD Display/ Wall Mountable/ 11 Days Standby Time/ 5 Hours Talk Time/ Silver Finish', 29.99); +INSERT INTO Product( title, description, price) VALUES ( 'Polk Audio White Round Two-Way In-Wall Loudspeaker - TC60I', 'Polk Audio White Round Two-Way In-Wall Loudspeaker - TC60I/ Dynamic Balance Polymer Composite Driver/ Aimable 1'' Silk Dome Tweeter With Neodymium Magnet/ 15-Degree Offset Drive Unit/ Wide Dispersion Design/ Durable, Moisture Resistant Materials/ Infinite Baffle Tuning/ Paintable, Powder-Coated Aluminum Grilles/ Conveniently Accessible Front Panel Controls/ Each Speaker Sold Seperately/ White Finish', 299.95); +INSERT INTO Product( title, description, price) VALUES ( 'Toshiba Black DVD/VCR Combinaton Player - SDV296', 'Toshiba Black DVD/VCR Combinaton Player - SDV296/ Progressive Scan DVD Player/ One Touch Recording For The VCR/ ColorStream Pro Progressive Scan Component Video Outputs/ Simultaneous DVD Playback And VHS Record/ JPEG Viewer/ Black Finish', 89.00); +INSERT INTO Product( title, description, price) VALUES ( 'Samsung 19'' Black Flat Panel Series 6 LCD HDTV - LN19A650', 'Samsung 19'' Black Flat Panel Series 6 LCD HDTV - LN19A650/ 1440 x 900 True 720p Resolution/ 3,000:1 Dynamic Contrast Ratio/ SRS TruSurround XT/ Built-In Digital Tuner (ATSC/Clear QAM)/ Wide Color Enhancer/ 8ms Response Time/ Touch Of Color Design/ Black With Red Finish', 499.00); +INSERT INTO Product( title, description, price) VALUES ( 'Samsung 22'' Black Flat Panel Series 6 LCD HDTV - LN22A650', 'Samsung 22'' Black Flat Panel Series 6 LCD HDTV - LN22A650/ 1680 x 1050 True 720p Resolution/ 5,000:1 Dynamic Contrast Ratio/ SRS TruSurround XT/ Built-In Digital Tuner (ATSC/Clear QAM)/ Wide Color Enhancer/ 8ms Response Time/ Touch Of Color Design/ Black With Red Finish', 599.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Deluxe Burgundy Leather Case - 2350B001', 'Canon Deluxe Burgundy Leather Case - 2350B001/ Genuine Leather Case/ Designed For The PowerShot SD770 IS, SD1100 And SD1000/ Burgundy Finish', 18.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Deluxe Black Digital Camera Case - 2595B002', 'Canon Deluxe Black Digital Camera Case - 2595B002/ Soft Nylon Case/ Flip-Down Cover/ Belt Loop Attachment For Hands-Free Convenience/ Compatible With Canon PowerShot A Series/ Black Finish', 13.99); +INSERT INTO Product( title, description, price) VALUES ( 'Griffin iPod RoadTrip With SmartScan - 4040RDTRPB', 'Griffin iPod RoadTrip With SmartScan - 4040RDTRPB/ Play Music From Your iPod On Your Car?s Stereo System As It Charges/ Switch Quickly Between Pre-Sets/ SmartSound Plus Technology Delivers Clear Sound Under Real-World Conditions/ Flexible Steel Neck/ Black Finish', 89.00); +INSERT INTO Product( title, description, price) VALUES ( 'Denon Black Home Theater Surround Sound Receiver - AVR1709', 'Denon Black AVR-1709 Home Theater Surround Sound Receiver - AVR1709/ 80 Watts Per Channel/ 7 Channels/ HDMI V1.3A Video Switching/ Dolby Digital Surround EX, Dolby Pro Logic IIx, DTS ES 6.1, DTS Neo:6 Decoding/ Audyssey MultEQ, Dynamic Volume And Dynamic EQ/ Video Up-Conversion/ Black Finish', 449.99); +INSERT INTO Product( title, description, price) VALUES ( 'Denon Black AVR-1609 Home Theater Surround Sound Receiver - AVR1609', 'Denon Black AVR-1609 Home Theater Surround Sound Receiver - AVR1609/ 75 Watts Per Channel/ 7 Channels/ HDMI V1.3A Video Switching/ Dolby Digital Surround EX, Dolby Pro Logic IIx, DTS ES 6.1, DTS Neo:6 Decoding/ Audyssey MultEQ, Dynamic Volume And Dynamic EQ/ Sirius Satellite Radio Ready/ Network And Digital Media Connectivity/ Black Finish', 349.99); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Bud Style Headphones In Silver - MDRED12LPSLV', 'Sony Bud Style Headphones In Silver - MDRED12LPSLV/ 16mm Drivers/ Crisp Sound/ Neodymium Magnets Offer Powerful Sound Reproduction/ Convenient Cord Slider Reduces Tangles/ 3.9 Ft. Cord Length/ Frequency Response Of 8-22,000Hz/ Silver Finish', 14.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony Bud Style Headphones In Red - MDRED12LPRED', 'Sony Bud Style Headphones In Red - MDRED12LPRED/ 16mm Drivers/ Crisp Sound/ Neodymium Magnets Offer Powerful Sound Reproduction/ Convenient Cord Slider Reduces Tangles/ 3.9 Ft. Cord Length/ Frequency Response Of 8-22,000Hz/ Red Finish', 14.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony EX Ear Bud Headphones In Black - MDREX32LPBLK', 'Sony EX Ear Bud Headphones In Black - MDREX32LPBLK/ 9mm Drivers/ Deep Bass Sound/ Neodymium (400kJ/m3) Magnets Offer Powerful Bass Sound Reproduction/ 3.9 Ft. Cord Length/ Frequency Response Of 6-23,000Hz/ Black Finish', 24.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony EX Ear Bud Headphones In White - MDREX32LPWHI', 'Sony EX Ear Bud Headphones In White - MDREX32LPWHI/ 9mm Drivers/ Deep Bass Sound/ Neodymium (400kJ/m3) Magnets Offer Powerful Bass Sound Reproduction/ 3.9 Ft. Cord Length/ Frequency Response Of 6-23,000Hz/ White Finish', 24.00); +INSERT INTO Product( title, description, price) VALUES ( 'Griffin iPhone SmartTalk - 3016SMRTLKB', 'Griffin iPhone SmartTalk - 3016SMRTLKB/ Adds A Microphone And An iPhone Control Button To Your Favorite Earphones/ Play, Pause And Skip Through Your Tunes/ High-Sensitivity Microphone For Crystal-Clear Phone Conversations/ 30'' Cable Sheathed In Nylon Braiding', 19.00); +INSERT INTO Product( title, description, price) VALUES ( 'Logitech Driving Force Pro Steering Wheel With Pedals Set For Sony Playstation 2 - 9632930403', 'Logitech Driving Force Pro Steering Wheel With Pedals Set For Sony Playstation 2 - 9632930403/ Comfortable Soft Full Rubber Wheel/ Realistically Turn Through 2.5 Times Wheel Rotation/ 900 Degrees Of wheel Steering/ State-Of-The-Art Force Feedback Technology/ Stick Shifter/ Responsive Gas And Brake Pedals/ Black Finish', 129.00); +INSERT INTO Product( title, description, price) VALUES ( 'Monster iCarPlay Wireless 250 FM Transmitter With AutoScan for iPod And iPhone - AIPFMCH250', 'Monster iCarPlay Wireless 250 FM Transmitter With AutoScan For iPod And iPhone - AIPFMCH250/ Plays iPod Or iPhone Music Over Any Car Stereo/ Vibrant Sound/ Works With Any FM Car Radio/ 3 Programmable Presets/ FM Stations Range From 88.1 To 107.9/ Easy To Use/ Convenient Charging While You Drive/ Black Finish', 100.00); +INSERT INTO Product( title, description, price) VALUES ( 'D-Link Broadband Cable Modem - DCM202', 'D-Link Broadband Cable Modem - DCM202/ DOCSIS 2.0 CableLabs Certified/ High-Speed Internet Connectivity/ Always On And Always Connected/ Ethernet Or USB Connectivity/ Front Panel LED Indicator Lights/ Grey Finish', 79.00); +INSERT INTO Product( title, description, price) VALUES ( 'LaCie USB 2.0 Floppy Disk Drive - 706018', 'LaCie USB 2.0 Floppy Disk Drive - 706018/ Ultra-Thin Portable Design/ Compatible With Windows And Mac OS/ Plug And Play/ USB Powered/ 250 - 500 kbps Transfer Rate/ Silver Finish', 49.00); +INSERT INTO Product( title, description, price) VALUES ( 'Toshiba XDE Black 1080p Upconversion Extended Detail DVD Player - XDE500', 'Toshiba XDE Black 1080p Upconversion Extended Detail DVD Player - XDE500/ Full 1080p Upconversion With 24 Frames Per Second/ Detail Enhancement/ Intelligent Color/ Contrast Enhancement/ DivX Certified/ Black Finish', 99.00); +INSERT INTO Product( title, description, price) VALUES ( 'Griffin Black iPhone 3G Wave Case - 8227IP2WVB', 'Griffin Black iPhone 3G Wave Case - 8227IP2WVB/ Elegtant Wave-Shaped Closures/ Durable Polycarbonate Protection/ Rigid Touchscreen Protector/ Full Access To All Ports And Controls/ Black Finish', 24.00); +INSERT INTO Product( title, description, price) VALUES ( 'iHome iPod & iPhone Clock Radio & Audio System - IP99BR', 'iHome iP99 iPod & iPhone Clock Radio & Audio System - IP99BR/ Universal Dock For iPhone/ Auto-Set Clock/ Programmable Snooze/ Charges iPod Or iPhone While Docked/ Reason8 Speaker Chambers/ Line In Jack/ Full Function Remote Control Included/ Dual Alarm Clock/ Extra-Large LCD Display/ Black Finish (iPhone Not Included)', 149.00); +INSERT INTO Product( title, description, price) VALUES ( 'Transcend 8GB SDHC Card And Compact Card Reader - TS8GSDHC6S5W', 'Transcend 8GB SDHC Card And Compact Card Reader - TS8GSDHC6S5W/ SDHC Card Is Class 6 Compliant And Compatible With All SDHC-Labeled Host Devices/ Card Reader Is Fully Compatible With Hi Speed USB 2.0, Up To 480Mb/s And Supports SDHC Memory Cards', 26.30); +INSERT INTO Product( title, description, price) VALUES ( 'Denon 7.1 Channel AV Receiver With Network Client Compatible D-Dock Port In Black - AVR2809CI', 'Denon 7.1 Channel AV Home Theater Surround Receiver With Network Client Compatible D-Dock Port In Black - AVR2809CI/ 110 Watts x 7 Channels/ 1080p Upscaling/ PC Setup And Control Capability Via RS-232C/ Network Capable/ XM Satellite Radio Ready/ Dolby TrueHD And DTS-HD Master Audio/ HDMI 1.3a Repeater Inputs-Outputs/ Dual Remotes/ Black Finish', 1199.99); +INSERT INTO Product( title, description, price) VALUES ( 'LaCie Little Disk 320GB Black Portable Hard Drive - 301829', 'LaCie Little Disk 320GB Black Portable Hard Drive - 301829/ Compact, Thin And Lightweight Design/ Back Up, Synchronize And Secure Files And Settings/ Extractable Integrated USB Cable And Protective Cap/ Hi-Speed USB 2.0/ PC And Mac Compatible/ Black Finish', 119.00); +INSERT INTO Product( title, description, price) VALUES ( 'LaCie Little Disk 250GB Black Portable Hard Drive - 301278', 'LaCie Little Disk 250GB Black Portable Hard Drive - 301278/ Compact, Thin And Lightweight Design/ Back Up, Synchronize And Secure Files And Settings/ Extractable Integrated USB Cable And Protective Cap/ Hi-Speed USB 2.0/ PC And Mac Compatible/ Black Finish', 99.00); +INSERT INTO Product( title, description, price) VALUES ( 'AppleCare Protection Plan For iPod Touch Or iPod Classic - MB591LLA', 'AppleCare Protection Plan For iPod Touch Or iPod Classic - MB591LLA/ Extends Your Service Coverage To Up To Two Years/ Includes Both Phone And In Store Techinical Support', 59.00); +INSERT INTO Product( title, description, price) VALUES ( 'Nikon CoolPix S610 10 Megapixel Black Digital Camera - COOLPIXS610BK', 'Nikon CoolPix S610 10 Megapixel Black Digital Camera - COOLPIXS610BK/ 10.0 Megapixels/ 4x Zoom-NIKKOR Lens/ 3.0'' High-Resolution Wide-Viewing Angle LCD Monitor/ Scene Auto Selector/ Active Child Mode/ Smile And Food Mode/ Face-Priority AF/ In-Camera Red-Eye Fix/ D-Lighting/ Midnight Black Finish', 249.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony VAIO Black USB Docking Station - VGPUPR1', 'Sony VAIO Black USB Docking Station - VGPUPR1/ Perfect For The Constant Traveler Or Mobile Professional/ Easily Connect All Of Necessary Peripherals/ VGA Port/ 4 USB Ports/ Ethernet Port/ Headphone And Microphone Ports/ Compatible With VAIO CR Series, FZ Series, FW Series, NR Series And AR Series Notebooks/ Black Finish', 199.99); +INSERT INTO Product( title, description, price) VALUES ( 'Nikon SB-900 AF Speedlight In Black - SB900', 'Nikon AF Speedlight In Black - SB900/ Wireless Commander Mode/ Control Up To Three Remote/ 3 Light Distribution Patterns/ Flash Tube Overheat Protection/ Drip-Proof Mounting Foot Cover (Water Guard)/ Color Gel Filter Identification/ Black Finish', 499.00); +INSERT INTO Product( title, description, price) VALUES ( 'Denon D-M37 Black CD/AM/FM Micro System - DM37SBK', 'Denon D-M37 Black CD/AM/FM Micro System - DM37SBK/ 30 Watts x 2 Amplifier/ Precision Burr-Brown Audiophile Quality DACs/ MP3 And WMA Tracks Playback Capability/ European Engineered Loudspeakers/ 120mm Long-Throw D.D.L Double-Layered Cone Woofer/ 25mm Soft Dome Tweeter With Extended Response/ Triadic Noise Reduction Concept/ Portable Player Connectivity/ Black Finish', 399.00); +INSERT INTO Product( title, description, price) VALUES ( 'Griffin iPhone 3G Black Elan Form Hard-Shell Leather Case - 8223IP2EFRMB', 'Griffin iPhone 3G Black Elan Form Hard-Shell Leather Case - 8223IP2EFRMB/ Top-Grain Outer Shell Crafted From Hand-Matched Leather/ Protective Polycarbonate Inner Shell/ Easy Access To Controls/ Includes Stiff Polycarbonate Screen Protector & Premium Cleaning Cloth/ Black Finish (iPhone Not Included)', 24.00); +INSERT INTO Product( title, description, price) VALUES ( 'Speck Black ToughSkin Case For iPhone 3G - IPH3GBLKTS', 'Speck Black ToughSkin Case For iPhone 3G - IPH3GBLKTS/ Tough, Textured And Ruggedized Protection/ Bottom Hinges Open To Allow Docking/ Thicker Corners For Extra Protection/ Removable Rotating Belt Clip/ Lightweight Design/ Easy Access To All Ports & Controls/ Black Finish (iPhone Not Included)', 34.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon PIXMA iP2600 Photo Printer - IP2600', 'Canon PIXMA Photo Printer - IP2600/ 4800 x 1200 Color dpi/ Spectacular Resolution/ Fast Photo Printing/ FINE Technology/ 1,472 Precision Nozzles/ Auto Image Fix/ Easy-PhotoPrint EX Software/ 4 In 1 Or 2 In 1 Printing/ Energy Star Qualified/ Borderless 4'' x 6'' Print Approx. 55 Seconds/ Windows And Mac Compatible', 49.00); +INSERT INTO Product( title, description, price) VALUES ( 'Klipsch 5.25'' THX Ultra2 In-Ceiling White Loudspeaker - KS7502THX', 'Klipsch 5.25'' THX Ultra2 In-Ceiling White Loudspeaker - KS7502THX/ 100W Continuous/400W Peak Power Handling/ Dual 1'' Titanium Diaphragm Compression Drivers Mated To WDST Tractrix Horn Array/ Dual 5.25'' High-Output Cerametallic Cone Woofers/ MDF And Aluminum Motorboard/ABS Shell Enclosure/ White Finish/ Price Per Speaker', 1000.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon PIXMA Photo All-In-One Printer - MP620', 'Canon PIXMA Photo All-In-One Printer - MP620/ High Performance Printer And Copier, Scanner/ Easy Scroll Wheel/ Dual Paper Trays/ 2.5'' High Definition LCD Display/ Maximum 9600 x 2400 Color Dpi/ Automatic Image Optimization/ Quick Start/ Wi-Fi Ready/ Built-In Media Card Slot/ ENERGY STAR Qualified', 149.00); +INSERT INTO Product( title, description, price) VALUES ( 'TiVo HD XL Black Digital Video Recorder - TCD658000', 'TiVo HD XL Black Digital Video Recorder - TCD658000/ Search, Record And Watch Shows In HD/ Save Up To 150 Hours Of HD Programming At A Time/ Control Cable TV With Pause, Rewind, Fast-Forward, And Slow-Motion/ Record Two Shows At Once In HD/ Digital Transition Ready/ Backlit Remote Control/ Netflix Instant Streaming/ TiVo Service Required And Sold Separately', 599.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple 8GB Black 2nd Generation iPod Touch - MB528LLA', 'Apple 8GB Black 2nd Generation iPod Touch - MB528LLA/ Holds Up To 1,750 Songs In 128-Kbps AAC Format, 10,000 iPod-Viewable Photos And 10 Hours Of Video/ Wi-Fi (802.11b/g)/ Nike + iPod Support Built-In/ Maps Location-Based Service/ 3.5'' (Diagonal) Widescreen Multi-Touch Display/ 480x320-Pixel Resolution/ 480p And 576p Component TV Out/ Mac And Windows Compatible/ Black Finish', 229.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple 16GB Black 2nd Generation iPod Touch - MB531LLA', 'Apple 16GB Black 2nd Generation iPod Touch - MB531LLA/ Holds Up To 3,500 Songs In 128-Kbps AAC Format, 20,000 iPod-Viewable Photos And 20 Hours Of Video/ Wi-Fi (802.11b/g)/ Nike + iPod Support Built-In/ Maps Location-Based Service/ 3.5'' (Diagonal) Widescreen Multi-Touch Display/ 480x320-Pixel Resolution/ 480p And 576p Component TV Out/ Mac And Windows Compatible/ Black Finish', 299.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple 32GB Black 2nd Generation iPod Touch - MB533LLA', 'Apple 32GB Black 2nd Generation iPod Touch - MB533LLA/ Holds Up To 7,000 Songs In 128-Kbps AAC Format, 25,000 iPod-Viewable Photos And 40 Hours Of Video/ Wi-Fi (802.11b/g)/ Nike + iPod Support Built-In/ Maps Location-Based Service/ 3.5'' (Diagonal) Widescreen Multi-Touch Display/ 480x320-Pixel Resolution/ 480p And 576p Component TV Out/ Mac And Windows Compatible/ Black Finish', 399.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple 8GB Silver 4th Generation iPod Nano - MB598LLA', 'Apple 8GB Silver 4th Generation iPod Nano - MB598LLA/ Holds Up To 2,000 Songs In 128-Kbps AAC Format, 7,000 iPod-Viewable Photos And 8 Hours Of Video/ 2'' (Diagonal) Liquid Crystal Display With Blue-White LED Backlight/ 320-By-240-Pixel Resolution/ Give It A Shake To Shuffle Your Music/ Turn It Sideways To View Cover Flow/ Mac And Windows Compatible/ Silver Finish', 144.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple 8GB Blue 4th Generation iPod Nano - MB732LLA', 'Apple 8GB Blue 4th Generation iPod Nano - MB732LLA/ Holds Up To 2,000 Songs In 128-Kbps AAC Format, 7,000 iPod-Viewable Photos And 8 Hours Of Video/ 2'' (Diagonal) Liquid Crystal Display With Blue-White LED Backlight/ 320-By-240-Pixel Resolution/ Give It A Shake To Shuffle Your Music/ Turn It Sideways To View Cover Flow/ Mac And Windows Compatible/ Blue Finish', 149.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple 8GB Pink 4th Generation iPod Nano - MB735LLA', 'Apple 8GB Pink 4th Generation iPod Nano - MB735LLA/ Holds Up To 2,000 Songs In 128-Kbps AAC Format, 7,000 iPod-Viewable Photos And 8 Hours Of Video/ 2'' (Diagonal) Liquid Crystal Display With Blue-White LED Backlight/ 320-By-240-Pixel Resolution/ Give It A Shake To Shuffle Your Music/ Turn It Sideways To View Cover Flow/ Mac And Windows Compatible/ Pink Finish', 144.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple 8GB Purple 4th Generation iPod Nano - MB739LLA', 'Apple 8GB Purple 4th Generation iPod Nano - MB739LLA/ Holds Up To 2,000 Songs In 128-Kbps AAC Format, 7,000 iPod-Viewable Photos And 8 Hours Of Video/ 2'' (Diagonal) Liquid Crystal Display With Blue-White LED Backlight/ 320-By-240-Pixel Resolution/ Give It A Shake To Shuffle Your Music/ Turn It Sideways To View Cover Flow/ Mac And Windows Compatible/ Purple Finish', 149.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple 8GB Green 4th Generation iPod Nano - MB745LLA', 'Apple 8GB Green 4th Generation iPod Nano - MB745LLA/ Holds Up To 2,000 Songs In 128-Kbps AAC Format, 7,000 iPod-Viewable Photos And 8 Hours Of Video/ 2'' (Diagonal) Liquid Crystal Display With Blue-White LED Backlight/ 320-By-240-Pixel Resolution/ Give It A Shake To Shuffle Your Music/ Turn It Sideways To View Cover Flow/ Mac And Windows Compatible/ Green Finish', 149.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple 8GB Black 4th Generation iPod Nano - MB754LLA', 'Apple 8GB Black 4th Generation iPod Nano - MB754LLA/ Holds Up To 2,000 Songs In 128-Kbps AAC Format, 7,000 iPod-Viewable Photos And 8 Hours Of Video/ 2'' (Diagonal) Liquid Crystal Display With Blue-White LED Backlight/ 320-By-240-Pixel Resolution/ Give It A Shake To Shuffle Your Music/ Turn It Sideways To View Cover Flow/ Mac And Windows Compatible/ Black Finish', 144.00); +INSERT INTO Product( title, description, price) VALUES ( 'Apple 1GB Pink 2nd Generation iPod Shuffle - MB811LLA', 'Apple 1GB Pink 2nd Generation iPod Shuffle - MB811LLA/ Holds Up To 240 Songs In 128-Kbps AAC Format/ 12 Hours Of Continuous Playback/ Skip-Free Playback/ Battery Indicator/ Shuffle Switch/ Built-In Clip/ Mac And Windows Compatible/ Pink Finish', 49.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sony BRAVIA Black SXRD 1080p Home Theater Front Projector - VPLHW10', 'Sony BRAVIA Black SXRD 1080p Home Theater Front Projector - VPLHW10/ SXRD 1920 x 1080p Full HD Panels/ 30,000:1 Dynamic Contrast Ratio/ Fully Digital Signal Processing/ 200Watts Ultra-High-Pressure Lamp/ Whisper-Quiet Fan Noise And Noise Reduction Function/ Two HDMI Inputs/ BRAVIA Theatre Sync/ Remote Commander/ Black Finish', 3499.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon PIXMA Black Photo Printer - IP4600', 'Canon PIXMA Black Photo Printer - IP4600/ Premium Printer With Individual Ink Tanks And Built-In Auto Duplex/ Print 4'' x 6'' Photo In 20 Seconds/ Dual Paper Trays/ Maximum 9600 x 2400 Color Dpi/ Automatic Image Optimization/ ENERGY STAR Qualified/ Black Finish', 99.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon PIXMA Photo All-In-One Printer - MP980', 'Canon PIXMA Photo All-In-One Printer - MP980/ High Performance Wireless Printer, Copier And Scanner/ Easy Scroll Wheel/ 3.5'' High Definition LCD Display/ Maximum 9600 x 2400 Color Dpi/ Six Individual Inks Tanks/ Auto Duplex Print/ Smart Copying/ Automatic Image Optimization/ No Warm-Up/ ENERGY STAR Qualified', 299.00); +INSERT INTO Product( title, description, price) VALUES ( 'Nintendo DS Lite Cobalt/Black Portable Gaming System - NDSUSGBMKB', 'Nintendo DS Lite Cobalt/Black Portable Gaming System - NDSUSGBMKB/ Dual 3'' TFT Color LCD Touchscreens/ Slimmer Design/ Dual Slot Compatibility (DS Lite/Game Boy Advance Game Paks)/ Twin Ultra Bright LCD Screens/ Up To 19 Hours Continuous Gameplay/ Nintendo Wi-Fi Connection/ Impressive 3D Graphics/ Dual Stereo Speakers/ Cobalt and Black Finish', 139.00); +INSERT INTO Product( title, description, price) VALUES ( 'Nintendo DS Lite Onyx Black Portable Gaming System - NDSUSGSKB', 'Nintendo DS Lite Onyx Black Portable Gaming System - NDSUSGSKB/ Dual 3'' TFT Color LCD Touchscreens/ Slimmer Design/ Dual Slot Compatibility (DS Lite/Game Boy Advance Game Paks)/ Twin Ultra Bright LCD Screens/ Up To 19 Hours Continuous Gameplay/ Nintendo Wi-Fi Connection/ Impressive 3D Graphics/ Dual Stereo Speakers/ Onyx Black Finish', 139.00); +INSERT INTO Product( title, description, price) VALUES ( 'Nintendo DS Lite Metallic Silver Portable Gaming System - NDSUSGSVB', 'Nintendo DS Lite Metallic Silver Portable Gaming System - NDSUSGSVB/ Dual 3'' TFT Color LCD Touchscreens/ Slimmer Design/ Dual Slot Compatibility (DS Lite/Game Boy Advance Game Paks)/ Twin Ultra Bright LCD Screens/ Up To 19 Hours Continuous Gameplay/ Nintendo Wi-Fi Connection/ Impressive 3D Graphics/ Dual Stereo Speakers/ Metallic Silver Finish', 139.00); +INSERT INTO Product( title, description, price) VALUES ( 'Nintendo DS Lite Metallic Rose Portable Gaming System - NDSUSGSZPB', 'Nintendo DS Lite Metallic Rose Portable Gaming System - NDSUSGSZPB/ Dual 3'' TFT Color LCD Touchscreens/ Slimmer Design/ Dual Slot Compatibility (DS Lite/Game Boy Advance Game Paks)/ Twin Ultra Bright LCD Screens/ Up To 19 Hours Continuous Gameplay/ Nintendo Wi-Fi Connection/ Impressive 3D Graphics/ Dual Stereo Speakers/ Metallic Rose Finish', 139.00); +INSERT INTO Product( title, description, price) VALUES ( 'Case Logic Vertical Universal Leather BlackBerry Case - CLP104BB', 'Case Logic Vertical Universal Leather BlackBerry Case - CLP104BB/ 360 Degree Swivel Belt Clip/ Magnetic Closure/ Soft Internal Lining/ Expandable Elastic Sides/ Compatible With Most BlackBerrys/ Leather Fabric/ Black Finish', 15.00); +INSERT INTO Product( title, description, price) VALUES ( 'Nintendo DS Lite Crimson/Black Portable Gaming System - NDSUSGSRMKB', 'Nintendo DS Lite Crimson/Black Portable Gaming System - NDSUSGSRMKB/ Dual 3'' TFT Color LCD Touchscreens/ Slimmer Design/ Dual Slot Compatibility (DS Lite/Game Boy Advance Game Paks)/ Twin Ultra Bright LCD Screens/ Up To 19 Hours Continuous Gameplay/ Nintendo Wi-Fi Connection/ Impressive 3D Graphics/ Dual Stereo Speakers/ Crimson/Black Finish', 139.00); +INSERT INTO Product( title, description, price) VALUES ( 'BlueAnt Bluetooth Voice Control Headset - V1', 'BlueAnt Bluetooth Voice Control Headset - V1/ Voice Control User Interface/ Voice Isolation Technology/ Dual Microphones/ Pairs With Up To 8 Bluetooth Devices/ Up To 5 Hours Talk-Time/ 3 Charging Options', 119.00); +INSERT INTO Product( title, description, price) VALUES ( 'Netgear Prosafe 5 Port Gigabit Ethernet Desktop Switch - GS105NA', 'Netgear Prosafe 5 Port Gigabit Ethernet Desktop Switch - GS105NA/ Auto-Switching Ethernet Connection/ Supports Windows And Macintosh Platforms/ Dual Color LEDs/ AutoUplink Technology/ Compact Metal Case/ Fanless Design/ Plug-And-Play Installation', 55.00); +INSERT INTO Product( title, description, price) VALUES ( 'Jabra Bluetooth Headset - BT2070', 'Jabra Bluetooth Headset - BT2070/ Up To 5.5 Hours Talk Time/ Up To 200 Hours Standby Time/ Earhook Included/ Bluetooth 2.0+ EDR & eSCO Technology/ Auto-Pairing/ Discreet Light/ Answer/End, Redial And Voice Dial Features/ USB Micro-B, 5-Pin Charging Plug', 49.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon Printer Ink Cartridge 4 Colors Pack - 2946B004', 'Canon Printer Ink Cartridge 4 Colors Pack - 2946B004/ FINE Technology For Exceptional Sharpness And Detail/ Compatible With PIXMA iP3600, PIXMA iP4600, PIXMA MP620 And PIXMA MP980/ Includes 4 Ink Tanks (Black, Cyan, Magenta, Yellow)', 47.00); +INSERT INTO Product( title, description, price) VALUES ( 'Canon PIXMA Photo All-In-One Printer - MP480', 'Canon PIXMA Photo All-In-One Printer - MP480/ High Performance Printer, Copier And Scanner/ 1.8'' TFT Display/ Maximum 2400 x 4800 Color Dpi/ Smart Scanning/ Quick Start/ Built-In Media Card Slot/ ENERGY STAR Qualified', 99.00); +INSERT INTO Product( title, description, price) VALUES ( 'Sanus 30'' - 58'' VisionMount Flat Panel TV Black Tilting Wall Mount - LT25B1', 'Sanus 30'' - 58'' VisionMount Flat Panel TV Black Tilting Wall Mount - LT25B1/ Lateral Shift Adjustment/ Virtual Axis/ Height and Level Adjustments/ ClickStand/ ClickFit System/ Open Wall Plate/ Black Finish', 199.00); diff --git a/integration/quarkus-keycloak-authorization/src/test/java/com/devonfw/quarkus/keycloak/authorization/NativePolicyEnforcerIT.java b/integration/quarkus-keycloak-authorization/src/test/java/com/devonfw/quarkus/keycloak/authorization/NativePolicyEnforcerIT.java new file mode 100644 index 0000000..71482c3 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/test/java/com/devonfw/quarkus/keycloak/authorization/NativePolicyEnforcerIT.java @@ -0,0 +1,10 @@ +package com.devonfw.quarkus.keycloak.authorization; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +/** + * @author Pedro Igor + */ +@QuarkusIntegrationTest +public class NativePolicyEnforcerIT extends PolicyEnforcerTest { +} diff --git a/integration/quarkus-keycloak-authorization/src/test/java/com/devonfw/quarkus/keycloak/authorization/PolicyEnforcerTest.java b/integration/quarkus-keycloak-authorization/src/test/java/com/devonfw/quarkus/keycloak/authorization/PolicyEnforcerTest.java new file mode 100644 index 0000000..7c0c7b3 --- /dev/null +++ b/integration/quarkus-keycloak-authorization/src/test/java/com/devonfw/quarkus/keycloak/authorization/PolicyEnforcerTest.java @@ -0,0 +1,40 @@ +package com.devonfw.quarkus.keycloak.authorization; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.keycloak.client.KeycloakTestClient; +import io.restassured.RestAssured; + +@QuarkusTest +public class PolicyEnforcerTest { + static { + RestAssured.useRelaxedHTTPSValidation(); + } + + KeycloakTestClient keycloakClient = new KeycloakTestClient(); + + @Test + public void testAccessUserResource() { + + RestAssured.given().auth().oauth2(getAccessToken("alice")).when().get("/product/v1/users").then().statusCode(200); + RestAssured.given().auth().oauth2(getAccessToken("jdoe")).when().get("/product/v1/users").then().statusCode(200); + } + + @Test + public void testAccessAdminResource() { + + RestAssured.given().auth().oauth2(getAccessToken("alice")).when().get("/product/v1/admin").then().statusCode(403); + RestAssured.given().auth().oauth2(getAccessToken("jdoe")).when().get("/product/v1/admin").then().statusCode(403); + RestAssured.given().auth().oauth2(getAccessToken("admin")).when().get("/product/v1/admin").then().statusCode(200); + } + + /* + * @Test public void testPublicResource() { RestAssured.given() .when().get("/api/public") .then() .statusCode(204); } + */ + + private String getAccessToken(String userName) { + + return this.keycloakClient.getAccessToken(userName); + } +}