diff --git a/src/base.sh b/src/base.sh index f52fccc..be8a9b6 100644 --- a/src/base.sh +++ b/src/base.sh @@ -22,7 +22,6 @@ declare -Ax config {{ FUNCTIONS }} {{ COMMANDS }} -wx-start if [[ ! -z $1 ]] && [[ $(type -t wx-$1) == function ]] then wx-$1 $2 $3 $4 $5 $6 $7 $8 $9 diff --git a/src/commands/auto.sh b/src/commands/auto.sh index 6715757..e0bd444 100644 --- a/src/commands/auto.sh +++ b/src/commands/auto.sh @@ -1,8 +1,6 @@ wx-auto(){ wx-login - - echo " >> Auto << " - echo "------------------------------" + wx-header "Auto" wx-ssh-config-sync wx-ssh-keys-sync diff --git a/src/commands/help.sh b/src/commands/help.sh index ca42f49..81d3e96 100644 --- a/src/commands/help.sh +++ b/src/commands/help.sh @@ -1,7 +1,6 @@ wx-help(){ -echo " >> Help << " -echo "------------------------------" +wx-header "Help" echo -n " Usage: $0 COMMAND [OPTIONS] @@ -21,9 +20,13 @@ Common Commands: sync Sync sign Certificates +Authentication Commands: + login Login + logout Logout + Management Commands: auto Auto - login Login + install Install settings Settings "; diff --git a/src/commands/infra.sh b/src/commands/infra.sh index 67cc50f..6c1552c 100644 --- a/src/commands/infra.sh +++ b/src/commands/infra.sh @@ -2,8 +2,8 @@ wx-infra(){ wx-login wx-ssh-sign &> /dev/null - echo " >> Infra << " - echo "------------------------------" + wx-header "Infra" + wx-restricted INFRA_PATH="/home/cwchristerw/Desktop/Work in Progress/Programming/warengroup/infra" diff --git a/src/commands/install.sh b/src/commands/install.sh new file mode 100644 index 0000000..81212f6 --- /dev/null +++ b/src/commands/install.sh @@ -0,0 +1,19 @@ +wx-install(){ + if [[ -z $1 ]] + then + wx-header "Install" + fi + + wx-restricted + + if [[ -f "./wx" ]] && [[ -d "./src" ]] + then + podman run -it --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp docker.io/library/php:8-cli php generator.php &> /dev/null + mv wx.tmp wx &> /dev/null + chmod +x wx &> /dev/null + fi + + mkdir $HOME/bin &> /dev/null + curl https://git.waren.io/warengroup/wx/raw/branch/master/wx -o $HOME/bin/wx &> /dev/null + chmod +x $HOME/bin/wx &> /dev/null +} diff --git a/src/commands/login.sh b/src/commands/login.sh index 3383508..c01df39 100644 --- a/src/commands/login.sh +++ b/src/commands/login.sh @@ -3,9 +3,7 @@ wx-login(){ if [[ -z "$HOSTNAME" || ${#HOSTNAME} -lt 5 ]] then - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo "Status: Hostname Required" wx-stop fi @@ -23,9 +21,7 @@ wx-login(){ then ORG=cwchristerw else - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo "Status: Organization Required" wx-stop fi @@ -40,9 +36,7 @@ wx-login(){ then DOMAIN=christerwaren.fi else - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo "Status: Organization Unsupported" wx-stop fi @@ -59,9 +53,7 @@ wx-login(){ then HOSTNAME=$(hostname --fqdn) else - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo "Status: Hostname Required" wx-stop fi @@ -72,9 +64,7 @@ wx-login(){ then if [[ -z LOGNAME ]] then - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo "Status: Username Required" wx-stop else @@ -88,7 +78,8 @@ wx-login(){ VAULT_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$VAULT_DOMAIN/v1/sys/health) if [[ $VAULT_STATUS -eq 200 ]] then - if [[ -f "$HOME/.config/warengroup/config.json" ]] + + if [[ $USER != "root" && $USER != "local" && -f "$HOME/.config/warengroup/config.json" ]] then TOKEN="$(cat $HOME/.config/warengroup/config.json | jq -r .login.$ORG)" fi @@ -97,15 +88,16 @@ wx-login(){ if [[ ! -z $VAULT_LOGIN && ${#VAULT_LOGIN} == 95 ]] then config["login",${ORG}]=$VAULT_LOGIN - jq '.login.'$ORG' = "'$VAULT_LOGIN'"' $HOME/.config/warengroup/config.json &> $HOME/.config/warengroup/config.json.tmp - mv $HOME/.config/warengroup/config.json.tmp $HOME/.config/warengroup/config.json &> /dev/null + if [[ $USER != "root" && $USER != "local" ]] + then + jq '.login.'$ORG' = "'$VAULT_LOGIN'"' $HOME/.config/warengroup/config.json &> $HOME/.config/warengroup/config.json.tmp + mv $HOME/.config/warengroup/config.json.tmp $HOME/.config/warengroup/config.json &> /dev/null + fi else IDM_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$IDM_DOMAIN) if [[ $IDM_STATUS -eq 301 ]] then - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo $wxBold$ORG$wxNormal if [[ -z $USERNAME || $USERNAME == "root" || $USERNAME == "local" ]] @@ -133,15 +125,17 @@ wx-login(){ fi config["login",${ORG}]=$VAULT_LOGIN - jq '.login.'$ORG' = "'$VAULT_LOGIN'"' $HOME/.config/warengroup/config.json &> $HOME/.config/warengroup/config.json.tmp - mv $HOME/.config/warengroup/config.json.tmp $HOME/.config/warengroup/config.json &> /dev/null + + if [[ $USER != "root" && $USER != "local" ]] + then + jq '.login.'$ORG' = "'$VAULT_LOGIN'"' $HOME/.config/warengroup/config.json &> $HOME/.config/warengroup/config.json.tmp + mv $HOME/.config/warengroup/config.json.tmp $HOME/.config/warengroup/config.json &> /dev/null + fi wx-start fi else - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo $wxBold$ORG$wxNormal echo -n "Token: " @@ -162,18 +156,19 @@ wx-login(){ fi config["login",${ORG}]=$VAULT_LOGIN - jq '.login.'$ORG' = "'$VAULT_LOGIN'"' $HOME/.config/warengroup/config.json &> $HOME/.config/warengroup/config.json.tmp - mv $HOME/.config/warengroup/config.json.tmp $HOME/.config/warengroup/config.json &> /dev/null + + if [[ $USER != "root" && $USER != "local" ]] + then + jq '.login.'$ORG' = "'$VAULT_LOGIN'"' $HOME/.config/warengroup/config.json &> $HOME/.config/warengroup/config.json.tmp + mv $HOME/.config/warengroup/config.json.tmp $HOME/.config/warengroup/config.json &> /dev/null + fi wx-start fi fi else - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo $wxBold$ORG$wxNormal - echo "Status: Vault Offline" wx-stop fi diff --git a/src/commands/logout.sh b/src/commands/logout.sh new file mode 100644 index 0000000..82da082 --- /dev/null +++ b/src/commands/logout.sh @@ -0,0 +1,3 @@ +wx-logout(){ + wx-header "Logout" +} diff --git a/src/commands/settings.sh b/src/commands/settings.sh index e1e08c7..c0c0cf2 100644 --- a/src/commands/settings.sh +++ b/src/commands/settings.sh @@ -1,6 +1,4 @@ wx-settings(){ wx-login - - echo " >> Settings << " - echo "------------------------------" + wx-header "Settings" } diff --git a/src/commands/ssh.sh b/src/commands/ssh.sh index 2d7ba3e..0fd5426 100644 --- a/src/commands/ssh.sh +++ b/src/commands/ssh.sh @@ -15,8 +15,7 @@ wx-ssh(){ wx-ssh-config $2 ;; *) - echo " >> SSH << " - echo "------------------------------" + wx-header "SSH" wx-stop ;; diff --git a/src/commands/ssh/config.sh b/src/commands/ssh/config.sh index a4bcf26..98aa0e1 100644 --- a/src/commands/ssh/config.sh +++ b/src/commands/ssh/config.sh @@ -1,6 +1,6 @@ wx-ssh-config(){ - echo " >> SSH / Config << " - echo "------------------------------" + wx-header "SSH / Config" + wx-restricted case $1 in edit) @@ -22,14 +22,17 @@ wx-ssh-config(){ } wx-ssh-config-edit(){ + wx-restricted nano ~/.ssh/config } wx-ssh-config-save(){ + wx-restricted curl https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/config -X POST --header "X-Vault-Token: ${config["login",$ORG]}" -d "{ \"data\": { \"data\": \"$(cat ~/.ssh/config | base64 -w 0)\" } }" -s &> /dev/null } wx-ssh-config-sync(){ + wx-restricted VAULT_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/config -X GET --header "X-Vault-Token: ${config["login",$ORG]}") if [[ $VAULT_STATUS -eq 200 ]] then diff --git a/src/commands/ssh/keys.sh b/src/commands/ssh/keys.sh index df43a08..de023b7 100644 --- a/src/commands/ssh/keys.sh +++ b/src/commands/ssh/keys.sh @@ -1,6 +1,5 @@ wx-ssh-keys(){ - echo " >> SSH / Keys << " - echo "------------------------------" + wx-header "SSH / Keys" case $1 in generate) @@ -28,6 +27,7 @@ wx-ssh-keys(){ } wx-ssh-keys-generate(){ + wx-restricted if [[ ! -z $1 ]] then if [[ ! -f "$HOME/.ssh/keys/$1" ]] @@ -38,6 +38,7 @@ wx-ssh-keys-generate(){ } wx-ssh-keys-retrieve(){ + wx-restricted if [[ ! -z $1 ]] then VAULT_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/keys/$1 -X GET --header "X-Vault-Token: ${config["login",$ORG]}") @@ -50,6 +51,7 @@ wx-ssh-keys-retrieve(){ } wx-ssh-keys-save(){ + wx-restricted if [[ ! -z $1 ]] then if [[ -f "$HOME/.ssh/keys/$1" ]] @@ -60,5 +62,6 @@ wx-ssh-keys-save(){ } wx-ssh-keys-sync(){ + wx-restricted echo "" } diff --git a/src/commands/ssh/sign.sh b/src/commands/ssh/sign.sh index a3549d2..7ee75af 100644 --- a/src/commands/ssh/sign.sh +++ b/src/commands/ssh/sign.sh @@ -1,6 +1,6 @@ wx-ssh-sign(){ - echo " >> SSH / Sign << " - echo "------------------------------" + wx-header "SSH / Sign" + wx-restricted if [[ $ORG == "warengroup" ]] then @@ -17,6 +17,8 @@ wx-ssh-sign(){ } wx-ssh-sign-create(){ + wx-restricted + NAME=$1 ROLE=$2 PRINCIPALS=$2 diff --git a/src/commands/update.sh b/src/commands/update.sh new file mode 100644 index 0000000..bd3c417 --- /dev/null +++ b/src/commands/update.sh @@ -0,0 +1,4 @@ +wx-update(){ + wx-header "Update" + wx-install --update +} diff --git a/src/commands/welcome.sh b/src/commands/welcome.sh index 1b1c77f..9c619b5 100644 --- a/src/commands/welcome.sh +++ b/src/commands/welcome.sh @@ -1,4 +1,3 @@ wx-welcome(){ - echo " >> Welcome << " - echo "------------------------------" + wx-header "Welcome" } diff --git a/src/functions/header.sh b/src/functions/header.sh new file mode 100644 index 0000000..d1f1e9c --- /dev/null +++ b/src/functions/header.sh @@ -0,0 +1,6 @@ +wx-header(){ + wx-start + + echo ">> $1 <<" + echo "------------------------------" +} diff --git a/src/functions/restricted.sh b/src/functions/restricted.sh new file mode 100644 index 0000000..8da98bb --- /dev/null +++ b/src/functions/restricted.sh @@ -0,0 +1,10 @@ +wx-restricted(){ + if [[ $USER == "root" || $USER == "local" ]] + then + echo "Status: Command Restricted" + echo " " + echo " " + echo " " + exit 1 + fi +} diff --git a/src/functions/stop.sh b/src/functions/stop.sh index f636fec..e2c0c4a 100644 --- a/src/functions/stop.sh +++ b/src/functions/stop.sh @@ -2,19 +2,8 @@ wx-stop (){ echo " " echo " " echo " " - if [[ -f "./wx" ]] && [[ -d "./src" ]] - then - podman run -it --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp docker.io/library/php:8-cli php generator.php &> /dev/null - mv wx.tmp wx &> /dev/null - chmod +x wx &> /dev/null - fi - if [[ $USER != "root" && $USER != "local" ]] - then - mkdir $HOME/bin &> /dev/null - curl https://git.waren.io/warengroup/wx/raw/branch/master/wx -o $HOME/bin/wx &> /dev/null - chmod +x $HOME/bin/wx &> /dev/null - fi + wx-install --auto exit 1 } diff --git a/wx b/wx index 460bdee..53b7e21 100755 --- a/wx +++ b/wx @@ -19,6 +19,24 @@ wxNormal=$(tput sgr0) declare -Ax config +wx-header(){ + wx-start + + echo ">> $1 <<" + echo "------------------------------" +} + +wx-restricted(){ + if [[ $USER == "root" || $USER == "local" ]] + then + echo "Status: Command Restricted" + echo " " + echo " " + echo " " + exit 1 + fi +} + wx-start(){ echo "" echo "" @@ -41,28 +59,15 @@ wx-stop (){ echo " " echo " " echo " " - if [[ -f "./wx" ]] && [[ -d "./src" ]] - then - podman run -it --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp docker.io/library/php:8-cli php generator.php &> /dev/null - mv wx.tmp wx &> /dev/null - chmod +x wx &> /dev/null - fi - if [[ $USER != "root" && $USER != "local" ]] - then - mkdir $HOME/bin &> /dev/null - curl https://git.waren.io/warengroup/wx/raw/branch/master/wx -o $HOME/bin/wx &> /dev/null - chmod +x $HOME/bin/wx &> /dev/null - fi + wx-install --auto exit 1 } wx-auto(){ wx-login - - echo " >> Auto << " - echo "------------------------------" + wx-header "Auto" wx-ssh-config-sync wx-ssh-keys-sync @@ -71,8 +76,7 @@ wx-auto(){ wx-help(){ -echo " >> Help << " -echo "------------------------------" +wx-header "Help" echo -n " Usage: $0 COMMAND [OPTIONS] @@ -92,9 +96,13 @@ Common Commands: sync Sync sign Certificates +Authentication Commands: + login Login + logout Logout + Management Commands: auto Auto - login Login + install Install settings Settings "; @@ -104,8 +112,8 @@ wx-infra(){ wx-login wx-ssh-sign &> /dev/null - echo " >> Infra << " - echo "------------------------------" + wx-header "Infra" + wx-restricted INFRA_PATH="/home/cwchristerw/Desktop/Work in Progress/Programming/warengroup/infra" @@ -138,14 +146,32 @@ wx-infra(){ esac } +wx-install(){ + if [[ -z $1 ]] + then + wx-header "Install" + fi + + wx-restricted + + if [[ -f "./wx" ]] && [[ -d "./src" ]] + then + podman run -it --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp docker.io/library/php:8-cli php generator.php &> /dev/null + mv wx.tmp wx &> /dev/null + chmod +x wx &> /dev/null + fi + + mkdir $HOME/bin &> /dev/null + curl https://git.waren.io/warengroup/wx/raw/branch/master/wx -o $HOME/bin/wx &> /dev/null + chmod +x $HOME/bin/wx &> /dev/null +} + wx-login(){ ORG=$1 if [[ -z "$HOSTNAME" || ${#HOSTNAME} -lt 5 ]] then - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo "Status: Hostname Required" wx-stop fi @@ -163,9 +189,7 @@ wx-login(){ then ORG=cwchristerw else - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo "Status: Organization Required" wx-stop fi @@ -180,9 +204,7 @@ wx-login(){ then DOMAIN=christerwaren.fi else - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo "Status: Organization Unsupported" wx-stop fi @@ -199,9 +221,7 @@ wx-login(){ then HOSTNAME=$(hostname --fqdn) else - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo "Status: Hostname Required" wx-stop fi @@ -212,9 +232,7 @@ wx-login(){ then if [[ -z LOGNAME ]] then - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo "Status: Username Required" wx-stop else @@ -228,7 +246,8 @@ wx-login(){ VAULT_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$VAULT_DOMAIN/v1/sys/health) if [[ $VAULT_STATUS -eq 200 ]] then - if [[ -f "$HOME/.config/warengroup/config.json" ]] + + if [[ $USER != "root" && $USER != "local" && -f "$HOME/.config/warengroup/config.json" ]] then TOKEN="$(cat $HOME/.config/warengroup/config.json | jq -r .login.$ORG)" fi @@ -237,15 +256,16 @@ wx-login(){ if [[ ! -z $VAULT_LOGIN && ${#VAULT_LOGIN} == 95 ]] then config["login",${ORG}]=$VAULT_LOGIN - jq '.login.'$ORG' = "'$VAULT_LOGIN'"' $HOME/.config/warengroup/config.json &> $HOME/.config/warengroup/config.json.tmp - mv $HOME/.config/warengroup/config.json.tmp $HOME/.config/warengroup/config.json &> /dev/null + if [[ $USER != "root" && $USER != "local" ]] + then + jq '.login.'$ORG' = "'$VAULT_LOGIN'"' $HOME/.config/warengroup/config.json &> $HOME/.config/warengroup/config.json.tmp + mv $HOME/.config/warengroup/config.json.tmp $HOME/.config/warengroup/config.json &> /dev/null + fi else IDM_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$IDM_DOMAIN) if [[ $IDM_STATUS -eq 301 ]] then - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo $wxBold$ORG$wxNormal if [[ -z $USERNAME || $USERNAME == "root" || $USERNAME == "local" ]] @@ -273,15 +293,17 @@ wx-login(){ fi config["login",${ORG}]=$VAULT_LOGIN - jq '.login.'$ORG' = "'$VAULT_LOGIN'"' $HOME/.config/warengroup/config.json &> $HOME/.config/warengroup/config.json.tmp - mv $HOME/.config/warengroup/config.json.tmp $HOME/.config/warengroup/config.json &> /dev/null + + if [[ $USER != "root" && $USER != "local" ]] + then + jq '.login.'$ORG' = "'$VAULT_LOGIN'"' $HOME/.config/warengroup/config.json &> $HOME/.config/warengroup/config.json.tmp + mv $HOME/.config/warengroup/config.json.tmp $HOME/.config/warengroup/config.json &> /dev/null + fi wx-start fi else - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo $wxBold$ORG$wxNormal echo -n "Token: " @@ -302,28 +324,31 @@ wx-login(){ fi config["login",${ORG}]=$VAULT_LOGIN - jq '.login.'$ORG' = "'$VAULT_LOGIN'"' $HOME/.config/warengroup/config.json &> $HOME/.config/warengroup/config.json.tmp - mv $HOME/.config/warengroup/config.json.tmp $HOME/.config/warengroup/config.json &> /dev/null + + if [[ $USER != "root" && $USER != "local" ]] + then + jq '.login.'$ORG' = "'$VAULT_LOGIN'"' $HOME/.config/warengroup/config.json &> $HOME/.config/warengroup/config.json.tmp + mv $HOME/.config/warengroup/config.json.tmp $HOME/.config/warengroup/config.json &> /dev/null + fi wx-start fi fi else - echo " >> Login << " - echo "------------------------------" - + wx-header "Login" echo $wxBold$ORG$wxNormal - echo "Status: Vault Offline" wx-stop fi } +wx-logout(){ + wx-header "Logout" +} + wx-settings(){ wx-login - - echo " >> Settings << " - echo "------------------------------" + wx-header "Settings" } wx-ssh(){ @@ -343,22 +368,25 @@ wx-ssh(){ wx-ssh-config $2 ;; *) - echo " >> SSH << " - echo "------------------------------" + wx-header "SSH" wx-stop ;; esac } +wx-update(){ + wx-header "Update" + wx-install --update +} + wx-welcome(){ - echo " >> Welcome << " - echo "------------------------------" + wx-header "Welcome" } wx-ssh-config(){ - echo " >> SSH / Config << " - echo "------------------------------" + wx-header "SSH / Config" + wx-restricted case $1 in edit) @@ -380,14 +408,17 @@ wx-ssh-config(){ } wx-ssh-config-edit(){ + wx-restricted nano ~/.ssh/config } wx-ssh-config-save(){ + wx-restricted curl https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/config -X POST --header "X-Vault-Token: ${config["login",$ORG]}" -d "{ \"data\": { \"data\": \"$(cat ~/.ssh/config | base64 -w 0)\" } }" -s &> /dev/null } wx-ssh-config-sync(){ + wx-restricted VAULT_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/config -X GET --header "X-Vault-Token: ${config["login",$ORG]}") if [[ $VAULT_STATUS -eq 200 ]] then @@ -396,8 +427,7 @@ wx-ssh-config-sync(){ } wx-ssh-keys(){ - echo " >> SSH / Keys << " - echo "------------------------------" + wx-header "SSH / Keys" case $1 in generate) @@ -425,6 +455,7 @@ wx-ssh-keys(){ } wx-ssh-keys-generate(){ + wx-restricted if [[ ! -z $1 ]] then if [[ ! -f "$HOME/.ssh/keys/$1" ]] @@ -435,6 +466,7 @@ wx-ssh-keys-generate(){ } wx-ssh-keys-retrieve(){ + wx-restricted if [[ ! -z $1 ]] then VAULT_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/keys/$1 -X GET --header "X-Vault-Token: ${config["login",$ORG]}") @@ -447,6 +479,7 @@ wx-ssh-keys-retrieve(){ } wx-ssh-keys-save(){ + wx-restricted if [[ ! -z $1 ]] then if [[ -f "$HOME/.ssh/keys/$1" ]] @@ -457,12 +490,13 @@ wx-ssh-keys-save(){ } wx-ssh-keys-sync(){ + wx-restricted echo "" } wx-ssh-sign(){ - echo " >> SSH / Sign << " - echo "------------------------------" + wx-header "SSH / Sign" + wx-restricted if [[ $ORG == "warengroup" ]] then @@ -479,6 +513,8 @@ wx-ssh-sign(){ } wx-ssh-sign-create(){ + wx-restricted + NAME=$1 ROLE=$2 PRINCIPALS=$2 @@ -497,7 +533,6 @@ wx-ssh-sign-create(){ } -wx-start if [[ ! -z $1 ]] && [[ $(type -t wx-$1) == function ]] then wx-$1 $2 $3 $4 $5 $6 $7 $8 $9