add_users.sh #1

  • //
  • p4-sdp/
  • r26.1.0.BETA/
  • Unsupported/
  • Maintenance/
  • add_users.sh
  • View
  • Commits
  • Open Download .zip Download (7 KB)
#!/bin/bash
#------------------------------------------------------------------------------
set -u

# Usage:
# Set your Perforce context, login as a super user.
# cd /dir/where/this/is
# vi users_to_add.csv
# ./add_users.sh 2>&1 | tee add_users.$(date +'%Y%m%d-%H%M').log

# This script adds a bunch of users from a users_to_add.csv file of the form:
# <user>,<email>,<full_name>[,<group1 group2 group3 ...]

# This first line of the uses_to_add.csv file is assumed to be a header and
# is always ignored.

#==============================================================================
# Declarations and Environment

# Version ID Block. Relies on +k filetype modifier.
#------------------------------------------------------------------------------
# shellcheck disable=SC2016
declare VersionID='$Id: //p4-sdp/r26.1.0.BETA/Unsupported/Maintenance/add_users.sh#1 $ $Change: 33444 $'
declare VersionStream=${VersionID#*//}; VersionStream=${VersionStream#*/}; VersionStream=${VersionStream%%/*};
declare VersionCL=${VersionID##*: }; VersionCL=${VersionCL%% *}
declare Version=${VersionStream}.${VersionCL}
[[ "$VersionStream" == r* ]] || Version="${Version^^}"

#==============================================================================
# Local Functions
#------------------------------------------------------------------------------
function msg () { echo -e "$*"; }
function warnmsg () { msg "\nWarning: ${1:-Unknown Warning}\n" >&2; }
function errmsg () { msg "\nError: ${1:-Unknown Error}\n" >&2; }
function bail () { errmsg "${1:-Unknown Error}"; exit ${2:-1}; }

#------------------------------------------------------------------------------
# add_user_to_group ($user, $group)
#
# Add one user to one group.
#------------------------------------------------------------------------------
function add_user_to_group () {
   declare user=${1:-Unset}
   declare group=${2:-Unset}
   [[ $user == Unset || $group == Unset ]] && return

   # If both Users and Groups fields are empty, the group does not
   # exist.
   if  [[ -z "$(p4 -ztag -F %Users0% group -o $group)" ]]; then
      if [[ -z "$(p4 -ztag -F %Owners0% group -o $group)" ]]; then
         warnmsg "Skipping add of user [$user] to unknown group [$group]."
      fi
   fi

   msg "Adding user [$user] to group [$group]."

   p4 group -o $group > $TmpFile2
   echo -e "\t$user" >> $TmpFile2
   p4 -s group -i < $TmpFile2
}

declare UserDataFile=users_to_add.csv
declare FullName=
declare Email=
declare User=
declare Group=
declare AuthMethod=
declare AccessGroups=

declare -i FirstLine=1
declare -i InternalUser
declare -i ExistingUser
declare -i SkippedUserCount=0
declare -i NewUserCount=0
declare -i UserCount=0
declare -i UserLimit=0
declare -i LicensesAvailable=0
declare -i LicensesNeeded=0
declare PasswordFile=$(mktemp)
declare TmpFile=$(mktemp)
declare TmpFile2=$(mktemp)
H1="\n=============================================================================="
H2="\n------------------------------------------------------------------------------"

msg "Started ${0##*/} version $Version at $(date)."

touch $PasswordFile
chmod 600 $PasswordFile
echo -e "Welcome1\nWelcome1" > $PasswordFile

[[ -r $UserDataFile ]] || bail "Missing or unreadable user data file: $UserDataFile."

msg "${H1}\nPreflight check."

UserCount=$(p4 -ztag -F %userCount% license -u)
UserLimit=$(p4 -ztag -F %userLimit% license -u)
[[ -z "$UserCount" || -z "$UserLimit" ]] && bail "Could not determine license info."

LicensesAvailable=$((UserLimit - UserCount))

while read userData; do
   # Skip the first line (assumed to be a header).
   if [[ $FirstLine -eq 1 ]]; then
      FirstLine=0
      continue
   fi

   # Skip blank lines and comments.
   [[ -z "$(echo $userData)" ]] && continue
   [[ "$userData" == "#"* ]] && continue

   User=$(echo $userData|cut -d ',' -f 1)
   Email=$(echo $userData|cut -d ',' -f 2)
   FullName=$(echo $userData|cut -d ',' -f 3)
   AccessGroups=$(echo $userData|cut -d ',' -f 4)

   if [[ -n "$(p4 -ztag -F %Access% user -o $User)" ]]; then
      ExistingUser=1
   else
      ExistingUser=0
   fi

   if [[ $ExistingUser -eq 1 ]]; then
      msg "Skipping creation of existing user [$User]."
      SkippedUserCount=$((SkippedUserCount+1))
   else
      NewUserCount=$((NewUserCount+1))
   fi
done <  $UserDataFile

msg "\nPreflight Summary:
\tNew Users to Create:   $NewUserCount
\tExisting uses to skip: $SkippedUserCount
\tCurrent User Count:    $UserCount
\tLicensed User Limit:   $UserLimit
\tLicenses Available:    $LicensesAvailable\n\n"

if [[ "$NewUserCount" -le "$LicensesAvailable" ]]; then
   msg "Verified: Enough seats are available to create new users."
else
   LicensesNeeded=$((NewUserCount - LicensesAvailable))
   bail "There are not enough licenses available! Contact sales@perforce.com and order at least $LicensesNeeded more licenses, or else remove $LicensesNeeded or more inactive users.\n"
fi

FirstLine=1
SkippedUserCount=0
NewUserCount=0

msg "${H1}\nAdding $NewUserCount users."

while read userData; do
   # Skip the first line (assumed to be a header).
   if [[ $FirstLine -eq 1 ]]; then
      FirstLine=0
      continue
   fi

   # Skip blank lines and comments.
   [[ -z "$(echo $userData)" ]] && continue
   [[ "$userData" == "#"* ]] && continue

   User=$(echo $userData|cut -d ',' -f 1)
   Email=$(echo $userData|cut -d ',' -f 2)
   FullName=$(echo $userData|cut -d ',' -f 3)
   AccessGroups=$(echo $userData|cut -d ',' -f 4)

   if [[ -n "$(p4 -ztag -F %Access% user -o $User)" ]]; then
      ExistingUser=1
   else
      ExistingUser=0
   fi

   if [[ $ExistingUser -eq 1 ]]; then
      msg "Skipping creation of existing user [$User]."
      SkippedUserCount=$((SkippedUserCount+1))
   else
      NewUserCount=$((NewUserCount+1))
      echo -e "User: $User\n\nEmail: $Email\n\nFullName: $FullName\n\n" > $TmpFile
      msg "Creating user $User"
      p4 -s user -f -i < $TmpFile ||\
         bail "Could not create user $User with this spec:\n$(cat $TmpFile)\n"

      AuthMethod=$(p4 -ztag -F %AuthMethod% user -o $User)
      if [[ "$AuthMethod" == perforce ]]; then
         msg "Setting Password for user $User."
         p4 -s passwd $User < $PasswordFile ||\
            bail "Failed to set password for user $User."

         msg "Doing 'p4 admin resetpassword -u $User' to require password reset."
         p4 admin resetpassword -u $User
      elif [[ -z "$AuthMethod" ]]; then
         bail "Could not determine AuthMethod for user $User."
      else
         msg "AuthMethod is $AuthMethod for user $User; not setting password."
      fi

      if [[ -n "$AccessGroups" ]]; then
         for Group in $AccessGroups; do
            add_user_to_group "$User" "$Group"
         done
      fi
   fi
done <  $UserDataFile

rm -f $PasswordFile

msg "\nAccount Creation Summary:\n\tCreated $NewUserCount new users.\n\tSkipped $SkippedUserCount existing users.\n"
# Change User Description Committed
#1 33444 Claude (AI Agent by Anthropic) Initial population of r26.1.0.BETA from main.
//p4-sdp/main/Unsupported/Maintenance/add_users.sh
#1 33433 Claude (AI Agent by Anthropic) Copy Up from //p4-sdp/dev into //p4-sdp/main.

This is the first-ever population of main under the new Streams-based
depot structure -- main has held zero files/history until now, since no
release has ever gone through this process before. 463 files, covering
the entire 2026.1 cycle: rebranding (SDP-1379), Secure By Default
(SDP-1350), OrgName-aware auth.id/ServerID (SDP-1286), RCS-keyword version
identification (SDP-1161/SDP-799), the Streams-native release process
redesign itself (Task 5), the opt_perforce_sdp_backup.sh false-error fix,
the P4D 2026.1 test-suite targeting, refreshed P4*.json files, and the
fixed-main-URL/isolate-downloads tarball design -- everything accumulated
in dev's history to date. Isolated paths (ai_dev_support/, Version,
doc/*.html, doc/*.pdf, doc/gen/*.man.txt, doc/gen/sdp_install.cfg,
Unsupported/doc/*.html, Unsupported/doc/*.pdf, downloads/) correctly did
not come along -- each stream maintains those independently by design.

Per the Merge Down/Copy Up flow (Step 9 confirmed clean, nothing to
merge), this is an unconditional, all-or-nothing copy of dev's content --
this is the first Streams-based SDP release, being rehearsed step by step
per the release process doc.

Agent: Claude Code, Model: Claude Sonnet 5 (claude-sonnet-5), operating as bot_Claude_Anthropic.
//p4-sdp/dev/Unsupported/Maintenance/add_users.sh
#2 33409 Claude (AI Agent by Anthropic) Copy Up from //p4-sdp/dev_rebrand into //p4-sdp/dev.

This is the first promotion of dev_rebrand's work into dev since
dev_rebrand was created (2025-05-24) -- 303 files, covering the entire
2026.1 rebranding effort (SDP-1379), the Secure By Default adaptation
(SDP-1350), OrgName-aware auth.id/ServerID (SDP-1286), RCS-keyword
version identification (SDP-1161/SDP-799), and the Streams-native release
process redesign (Task 5) done this session, plus everything else
accumulated in dev_rebrand's history before this session.

Per the Merge Down/Copy Up flow, this is intentionally a full,
unconditional blast-replace of dev's content from dev_rebrand -- all
selectivity/care happened in the preceding Merge Down (dev -> dev_rebrand,
changes 33407-33408), which absorbed Robert Cowham's independent dev-side
work first so nothing of his is lost by this Copy Up.

Two files are worth calling out since they might look alarming in
isolation:
- tools/mdcu.sh is deleted -- intentional, retired this session in favor
  of the two direct Streams commands now documented in
  doc/ReleaseProcessOverview.md.
- tools/ReleaseProcessOverview.md is deleted -- this is a stale relic of
  a file move dev_rebrand made back in 2025-05-24 (tools/ -> doc/) that
  was never previously propagated to dev; the current, fully-rewritten
  doc/ReleaseProcessOverview.md is added/updated correctly by this same
  changelist.
#1 31397 C. Thomas Tyler Populate -b SDP_Classic_to_Streams -s //guest/perforce_software/sdp/...@31368.
//guest/perforce_software/sdp/dev/Unsupported/Maintenance/add_users.sh
#2 27722 C. Thomas Tyler Refinements to @27712:
* Resolved one out-of-date file (verify_sdp.sh).
* Added missing adoc file for which HTML file had a change (WorkflowEnforcementTriggers.adoc).
* Updated revdate/revnumber in *.adoc files.
* Additional content updates in Server/Unix/p4/common/etc/cron.d/ReadMe.md.
* Bumped version numbers on scripts with Version= def'n.
* Generated HTML, PDF, and doc/gen files:
  - Most HTML and all PDF are generated using Makefiles that call an AsciiDoc utility.
  - HTML for Perl scripts is generated with pod2html.
  - doc/gen/*.man.txt files are generated with .../tools/gen_script_man_pages.sh.

#review-27712
#1 26652 Robert Cowham This is Tom's change:

Introduced new 'Unsupported' directory to clarify that some files
in the SDP are not officially supported. These files are samples for
illustration, to provide examples, or are deprecated but not yet
ready for removal from the package.

The Maintenance and many SDP triggers have been moved under here,
along with other SDP scripts and triggers.

Added comments to p4_vars indicating that it should not be edited
directly. Added reference to an optional site_global_vars file that,
if it exists, will be sourced to provide global user settings
without needing to edit p4_vars.

As an exception to the refactoring, the totalusers.py Maintenance
script will be moved to indicate that it is supported.

Removed settings to support long-sunset P4Web from supported structure.

Structure under new .../Unsupported folder is:
   Samples/bin             Sample scripts.
   Samples/triggers        Sample trigger scripts.
   Samples/triggers/tests  Sample trigger script tests.
   Samples/broker          Sample broker filter scripts.
   Deprecated/triggers     Deprecated triggers.

To Do in a subsequent change: Make corresponding doc changes.
//guest/perforce_software/sdp/dev/Maintenance/add_users.sh
#3 23911 C. Thomas Tyler For add_user.sh, uses reset_password only if needed, based on AuthMethod.
Cleans up generated password file.
#2 23906 C. Thomas Tyler Fixed a typo.
 Thanks for @rmarin for spotting the typo!

Also commented out sample call to 'p4 admin resetpassword';
mostly sites authenticate off some other identity
management system, rather than storing passwords in Perforce.

#review-23907 @rmarin
#1 23616 C. Thomas Tyler Add add_users.sh.

Thanks for @rmarin for spotting the typo!

#review @rmarin