PreviousNext

A Model For Task Objects

This topic examines the parts of the user task object that should be emulated in other task objects that you create for use with the DCE control program. Adhering to the basic model ensures that your task object will look and behave consistently with other parts of the dcecp program.

For efficiency and readability, the example does not include all of the procedures contained in the user task object. Furthermore, we have omitted some repetitive parts of the included procedures, replacing the omitted parts with vertical ellipses in the code examples. The entire user task object is contained in dcelocal/dcecp.

Name your object after the entity on which it operates rather than as a verb such as show or modify. DCE control program objects are named for the DCE entity on which they operate. Primitive objects like rpcentry and principal objects operate on single manageable DCE entities. Task objects operate at a higher level, generally invoking several primitive objects to achieve their goal. The authors of the user task object contrived a higher level entity - a user - as a manageable object.

The user object begins with the top level proc command and its argument table that defines the procedures and operations provided by the user object. Use the following syntax to define separate procedures in this argument table:

verb command function_call procedure_name "helptext_string"

The call to the parseargs procedure (defined in a separate file called parseargs.dcp) returns the name of the internal procedure that is to be called along with its arguments. The parseargs procedure is explained in Using the parseargs Procedure.

# proc user - This procedure is the front end for the user task

# scripts. All argument checking for the provided switches is done

# in the individual functions.

#

proc user { args } {

set arg_table {

{create command function_call _dcp_create_user

"Create a DCE user" }

{delete command function_call _dcp_delete_user

"Delete a DCE user"}

{show command function_call _dcp_show_user

"Show the attributes of a DCE user"}

{help help help_list

"Print summary of command-line options and abort"}

{operations operations operation_list

"Return valid operations for command."}}

set verbose_prose

"This object allows the manipulation of a DCE user. A user is represented as a principal and account with membership in a group and organization as well as having a directory in the CDS namespace. A user may be created, deleted or have attribute information returned. The argument is a list of either relative or fully qualified principal names. All fixed attributes of the principal and account object may be specified when creating a user. The -force option to the create verb allows the group or organization for that user to be created if necessary. The user is provided a directory in the CDS namespace, with the appropriate ACLs. Access to create a user requires the correct ACLs on principal, group and organization directories within the registry and the clearinghouse and users directory in the CDS namespace."


set local_args $args

parseargs $arg_table local_args -found_one

if { [info local help_prose ] > 0 } { return $help_prose }

if { [info local function_call ] > 0 } {

return [$function_call local_args]

} else {

error "\"user\" object requires a verb to form a command."

}

}

The next part of the script examines a procedure that takes many options or attributes as input - _dcp_create_user. While this procedure relies on numerous lower-level procedures to do the actual work of creating a user, the example begins by showing just one of the lower-level procedures; _dcp_create_principal_entry.

Then the script continues with the _dcp_create_user procedure. Notice that the name of this procedure (and all lower-level procedures) begins with an underscore. That's because the Tcl info command is frequently used to return the names of all procedures. This convention distinguishes these internal procedure names from procedures like user which are documented procedures. Furthermore, the _dcp part of the name distinguishes dcecp procedures from other Tcl procedures on a host.

The _dcp_create_user procedure has an argument table defining its available options. This argument table differs from the script's initial argument table in that it lacks the command keyword and the function_call variable that define separate procedures in the script.

Next it initializes variables entered either as options or as attributes in a list. A process_attribute_list procedure (at the end of the example) actually parses attributes that have been passed as a list. Then it does the work of creating the user information in the registry and in CDS. Near the end, a cleanup procedure _dcp_cleanup_user_create can undo a failed user create operation.

.

. several low-level procedures omitted

.

#

# This procedure creates a principal in the current registry _s(sec)

# if that principal does not yet exist.

#

proc _dcp_create_principal_entry { principal_name princ_args} {

set list_of_principals [principal catalog]

if { [lsearch $list_of_principals $principal_name] == -1} {

if { [llength $princ_args ] != 0 } {

principal create $principal_name -attribute $princ_args

} else

} else {

error "Principal \"$principal_name\" already exists."

}

}

#

# proc _dcp_create_user - This procedure actually creates a DCE user.

# Several steps are performed. If the principal does not exist

# a new one is created. If the groups do not exist and a -force switch is

# set, then two new groups will be added. The user will be added to the

# groups. The account will then be created. An entry in the CDS

# namespace will then be created with the appropriate ACL's.

#

proc _dcp_create_user { local_args } {

set arg_table {

{-alias string alias

"Add principal named as an alias of specified uid."}

{-attribute string attribute_list

"Provide attributes in an attribute list format."}

{-client string client

"Can the account principal be a client."}

{-description string descr

"A general description of the account."}

{-dupkey string dupkey

"Can the accounts's principal have duplicate keys."}

{-expdate string expdate

"When does the account expire."}

.

. [repetitive elements omitted]

.

{-uid integer uid

"User Identifier of the principal to be added."}}

#

# Initializing some variables.

#

upvar 1 local_args cargs

set local_args $cargs

set account_args ""

set princ_args ""

set group_args ""

set force 0

parseargs $arg_table local_args -no_leftovers

if { [info local help_prose ] > 0 } { return }

if { [llength $local_args] > 1 } {

error "Unrecognized argument [lindex $local_args 1]."

} elseif { [llength $local_args] == 0 } { error "No user name."

} else { set account_name $local_args }

#

# If parseargs returned attributes in a list instead of options,

# create an attribute list. Then call process_attribute_list to

# parse the list.

#

if { [info local attribute_list] > 0} {

set pile_of_attributes "alias client descr dupkey expdate\

forwadabletkt fullname force group home organization maxtktlife \

maxtktrenew mypwd password postdatedtkt proxiabletkt pwdvalid \

renewabletkt server quota shell stdgtauth"

process_attribute_list attribute_list $pile_of_attributes

}

#

# If user entered attributes as options rather than in a list,

# check for attribute options.

#

if { [info local group] > 0} {

set account_args [format "%s {%s %s}" $account_args group $group]

} else { error "No group name specified." }

if { [info local organization] > 0} {

set account_args [format "%s {%s %s}" $account_args organiz $organization]

} else { error "No organization name specified." }

if { [info local password] > 0} {

set account_args [format "%s {%s %s}" $account_args password $password]

} else { error "No password specified." }

if { [info local mypwd] > 0 } {

set account_args [format "%s {%s %s}" $account_args mypwd $mypwd]

} else { error "No admin password specified." }

#

# principal and group operations both use the principal's fullname

#

if { [info local fullname] > 0 } {

set princ_args [format "%s {%s {%s}}" $princ_args fullname $fullname]

set group_args [format "%s {%s {%s}}" $group_args fullname $fullname]

}

if { [info local uid] > 0 } {

set princ_args [format "%s {%s %s}" $princ_args uid $uid]

}

.

. [repetitive elements omitted]

.

if { [info local stdtgtauth] > 0 } {

set account_args [format "%s {%s %s}" $account_args stdtgtauth \

$stdtgtauth]

}

#

# set variables if entered as attributes in an attribute list

#

set account_name [lindex $account_name 0]

set group_created 0

set org_created 0

set group_arg ""

set org_arg ""

#

# do the work - create principal, do group and organization

# operations, create the account, and create directory in CDS

#

foreach element $account_name {

set clup_user "_dcp_cleanup_user_create $element -principal"

_dcp_create_principal_entry $element $princ_args

if { $force == 1 } {

if {[ catch {_dcp_create_group $group group_created} msg] != 0 } {

_dcp_cleanup_user_create $element -principal

error $msg

}

if { $group_created == 1 } {

set group_arg "-group group"

}

if {[ catch {_dcp_create_org $organization org_created} msg] != 0 } {

set clup_user [concat $clup_user $group_arg]

eval $clup_user

error $msg

}

if { $org_created == 1 } {

set org_arg "-org organization"

}

}

set clup_user [concat $clup_user $group_arg $org_arg]

if {[catch {_dcp_add_group_entry $group $element} msg] != 0} {

eval $clup_user

error $msg

}

if {[catch {_dcp_add_org_entry $organization $element} msg] != 0 } {

eval $clup_user

error $msg

}

if {[catch {_dcp_add_account_entry $element $account_args} msg] != 0} {

eval $clup_user

error $msg

}

if {[catch {_dcp_add_namespace_entry $element} msg] != 0} {

eval $clup_user

error $msg

}

}

set _n $account_name

return

}

#

# _dcp_cleanup_user_create - This function undoes changes after a

# failure in one of the user create functions as though the operation

# never occurred

#

proc _dcp_cleanup_user_create {account_name args} {

if { [lsearch $args -principal] != -1 } {

principal delete $account_name

}

if { [lsearch $args -group] != -1 } {

upvar 1 group clean_group

group delete $clean_group

}

if { [lsearch $args -org] != -1 } {

upvar 1 organization clean_org

organization delete $clean_org

}

}

#

# process_attribute_list - Takes an attribute_list and parses out the

# appropriate attributes contained in the

# pile_of_attributes variable

#

proc process_attribute_list {attribute_list pile_of_attributes} {

foreach element $pile_of_attributes { upvar 1 $element _dcp_$element }

upvar 1 attribute_list _dcp_attribute_list

set _dcp_attribute_list [check_list_list $_dcp_attribute_list]

foreach element $_dcp_attribute_list {

if { [llength $element] != 2 } {

error "Incorrect attribute list element \"$element\"."

}

set attribute_name [lindex $element 0]

set attribute_value [lindex $element 1]

set _dcp_attr_name [info vars _dcp_$attribute_name*]

if {[llength $_dcp_attr_name] > 1} {

error "Ambiguous attribute \"$attribute_name\" could be: $_dcp_attr_name."

}

set [set _dcp_attr_name] $attribute_value

}

}

proc check_list_list {attribute_list} {

set not_list_list 0

set i 1

foreach element $attribute_list {

if {[llength $element] != 2 && [llength $attribute_list] < 3} {

if {$i == 1} {

return [format "{%s}" $attribute_list]

}

}

incr i

}

return $attribute_list

}

The next procedure we discuss in the user task object is one that takes a single optional argument and returns lots of output information: the _dcp_show_user procedure. This procedure returns the results of principal show and account show operations.

#

# _dcp_show_user - This procedure shows the principal and account

# attribute lists for a specified user.

#

proc _dcp_show_user {local_args} {

upvar 1 local_args cargs

set local_args $cargs

parseargs "" local_args -no_leftovers

if { [info local help_prose ] > 0 } { return }

if { [llength $local_args] > 1 } {

error "Unrecognized argument [lindex $local_args 1]."

} elseif { [llength $local_args] == 0 } { error "No user name."

} else { set account_name $local_args }

# Take the first element of the account_name in order to

# eliminate list nesting.

set account_name [lindex $account_name 0]

set _dcp_principals [principal catalog -simplename]

# Show each account that has been requested.

foreach element $account_name {

if { [lsearch $_dcp_principals $element] == -1 } {

error "User \"$element\" does not exist."

} else {

set _dcp_user_attributes [principal show $element]

}

set _dcp_accounts [account catalog -simplename]

if { [lsearch $_dcp_accounts $element] == -1 } {

error "User \"$element\" does not exist."

} else {

set _dcp_user_attributes [format "%s\n%s" \

$_dcp_user_attributes \

[account show $element -all]]

}

}

return $_dcp_user_attributes

}