diff --git a/TopicUserMappingContrib/data/Main/AdminGroup.txt b/TopicUserMappingContrib/data/Main/AdminGroup.txt new file mode 100644 index 0000000000..6edf027bd8 --- /dev/null +++ b/TopicUserMappingContrib/data/Main/AdminGroup.txt @@ -0,0 +1,31 @@ +%META:TOPICINFO{author="ProjectContributor" date="1111931141" format="1.0" version="$Rev: 15508 $"}% +---+ Administrator Group + + + * Member list (comma-separated list): + * Set GROUP = + * Persons/group who can change the list: + * Set ALLOWTOPICCHANGE = AdminGroup + +%INCLUDE{"%USERSWEB%.AdminUser" section="sudo_login"}% + +%IF{"(NOT defined GROUP) OR $GROUP = ''" then="" else=""}% + +--- +*Related topics:* [[%WIKIUSERSTOPIC%]], [[AdminUser]], [[WikiGroups]], [[%SYSTEMWEB%.AccessControl][AccessControl]] + + diff --git a/TopicUserMappingContrib/data/Main/GroupTemplate.txt b/TopicUserMappingContrib/data/Main/GroupTemplate.txt new file mode 100644 index 0000000000..98ff1f7c7a --- /dev/null +++ b/TopicUserMappingContrib/data/Main/GroupTemplate.txt @@ -0,0 +1,10 @@ +%META:TOPICINFO{author="ProjectContributor" date="1111931141" format="1.0" version="$Rev: 16686 $"}% +---+!! %URLPARAM{"topic"}% + + * Member list (comma-separated list): + * Set GROUP = %WIKIUSERNAME% + * Persons/group who can change the list: + * Set ALLOWTOPICCHANGE = %URLPARAM{"topic"}% + +*%MAKETEXT{"Related topics:"}%* [[%SYSTEMWEB%.AccessControl][AccessControl]] + diff --git a/TopicUserMappingContrib/data/Main/NobodyGroup.txt b/TopicUserMappingContrib/data/Main/NobodyGroup.txt new file mode 100644 index 0000000000..302ff70ce8 --- /dev/null +++ b/TopicUserMappingContrib/data/Main/NobodyGroup.txt @@ -0,0 +1,11 @@ +%META:TOPICINFO{author="ProjectContributor" date="1111931141" format="1.0" version="$Rev: 16686 $"}% +---+ Nobody Group + + * Member list: + * Set GROUP = + * Persons/group who can change the list: + * Set ALLOWTOPICCHANGE = AdminGroup + +Used to prevent dangerous actions e.g. renaming [[%SYSTEMWEB%.DefaultPreferences][DefaultPreferences]] - put !NobodyGroup as the only group allowed to rename a topic and it can't be renamed. + +*Related topics:* [[%WIKIUSERSTOPIC%]], [[WikiGroups]], [[%SYSTEMWEB%.AccessControl][AccessControl]] diff --git a/TopicUserMappingContrib/data/Main/RegistrationAgent.txt b/TopicUserMappingContrib/data/Main/RegistrationAgent.txt new file mode 100644 index 0000000000..e7c8e4debd --- /dev/null +++ b/TopicUserMappingContrib/data/Main/RegistrationAgent.txt @@ -0,0 +1,8 @@ +%META:TOPICINFO{author="ProjectContributor" date="1111931141" format="1.0" version="$Rev: 16686 $"}% +---+ The RegistrationAgent User + +This is a user used by Foswiki when it registers new users. This user has special access to write to [[%WIKIUSERSTOPIC%]], and does not need an entry in the password system. + +*Related topics:* [[%WIKIUSERSTOPIC%]], [[%SYSTEMWEB%.UserRegistration][UserRegistration]] + +%META:PREFERENCE{name="ALLOWTOPICCHANGE" title="ALLOWTOPICCHANGE" type="Set" value="AdminGroup"}% diff --git a/TopicUserMappingContrib/data/Main/UserHomepageHeader.txt b/TopicUserMappingContrib/data/Main/UserHomepageHeader.txt new file mode 100644 index 0000000000..25dbe21b70 --- /dev/null +++ b/TopicUserMappingContrib/data/Main/UserHomepageHeader.txt @@ -0,0 +1,16 @@ +%META:TOPICINFO{author="ProjectContributor" date="1143332901" format="1.1" version="$Rev$"}% +---+ Header of User Homepages + +*Note:* This is a maintenance topic, used by the Wiki administrator. + +The part between the horizontal rules gets included at the top of every [[%WIKIUSERSTOPIC%]] homepage. The header can be customized to the needs of your organization. For example, show all frequently used fields from the [[%SYSTEMWEB%.UserForm][UserForm]] on top, followed by a personalized portal. The portal can pull content from other places, such as headline news by department, a list of current projects, etc. + +----- +%STARTINCLUDE% +%SEARCH{ "%BASETOPIC%" scope="topic" topic="%BASETOPIC%" nonoise="on" format="---+!! $formfield(FirstName) $formfield(LastName)$n()$n()| Organization: | $formfield(OrganisationName) | | Country: | $formfield(Country) | | Location: | $formfield(Location) |$n()| Telephone: | $formfield(Telephone) | | E-mail: | $formfield(Email) |" }% +%TOC% +%STOPINCLUDE% + +--- +*Related topics:* [[%WIKIUSERSTOPIC%]], [[%SYSTEMWEB%.UserForm][UserForm]], [[%SYSTEMWEB%.NewUserTemplate][NewUserTemplate]], [[%SYSTEMWEB%.UserRegistration][UserRegistration]], [[%SYSTEMWEB%.DataForms][DataForms]] + diff --git a/TopicUserMappingContrib/data/Main/UserList.txt b/TopicUserMappingContrib/data/Main/UserList.txt new file mode 100644 index 0000000000..aac1185841 --- /dev/null +++ b/TopicUserMappingContrib/data/Main/UserList.txt @@ -0,0 +1,6 @@ +%META:TOPICINFO{author="ProjectContributor" date="1163619832" format="1.0" version="$Rev: 15206 $"}% +---+ User List sorted by name + +%INCLUDE{"UserListHeader"}% + +%SEARCH{"form.name~'*UserForm'" nosearch="on" type="query" header="| *FirstName* | *LastName* | *Organization* | *State* | *Country* |" format="| [[$topic][$formfield(FirstName)]] | [[$topic][$formfield(LastName)]] | $formfield(OrganisationName) | $formfield(State) | $formfield(Country) |" excludetopic="Web*, *Template"}% diff --git a/TopicUserMappingContrib/data/Main/UserListByDateJoined.txt b/TopicUserMappingContrib/data/Main/UserListByDateJoined.txt new file mode 100644 index 0000000000..49a5f82384 --- /dev/null +++ b/TopicUserMappingContrib/data/Main/UserListByDateJoined.txt @@ -0,0 +1,7 @@ +%META:TOPICINFO{author="ProjectContributor" date="1163619832" format="1.0" version="$Rev: 15206 $"}% +---+ User List sorted by date joined / updated + +%INCLUDE{"UserListHeader"}% + +%TABLE{initsort="1" initdirection="up"}% +%SEARCH{"form.name~'*UserForm'" type="query" header="| *Date joined* | *Last updated* | *FirstName* | *LastName* | *Organization* | *Country* |" format="| $createdate | $date | [[$topic][$formfield(FirstName)]] | [[$topic][$formfield(LastName)]] | $formfield(OrganisationName) | $formfield(Country) |" excludetopic="Web*, *Template" nosearch="on"}% diff --git a/TopicUserMappingContrib/data/Main/UserListByLocation.txt b/TopicUserMappingContrib/data/Main/UserListByLocation.txt new file mode 100644 index 0000000000..2a06f52adb --- /dev/null +++ b/TopicUserMappingContrib/data/Main/UserListByLocation.txt @@ -0,0 +1,7 @@ +%META:TOPICINFO{author="ProjectContributor" date="1163619832" format="1.0" version="$Rev: 15206 $"}% +---+ User List sorted by location + +%INCLUDE{"UserListHeader"}% + +%TABLE{initsort="1"}% +%SEARCH{"form.name~'*UserForm'" type="query" header="|*Country* | *State* | *FirstName* | *LastName* | *Organization* |" format="| $formfield(Country) | $formfield(State) | [[$topic][$formfield(FirstName)]] | [[$topic][$formfield(LastName)]] | $formfield(OrganisationName) |" excludetopic="Web*, *Template" nosearch="on" order="formfield(Country)"}% diff --git a/TopicUserMappingContrib/data/Main/UserListHeader.txt b/TopicUserMappingContrib/data/Main/UserListHeader.txt new file mode 100644 index 0000000000..29c946179b --- /dev/null +++ b/TopicUserMappingContrib/data/Main/UserListHeader.txt @@ -0,0 +1,2 @@ +%META:TOPICINFO{author="ProjectContributor" date="1127307283" format="1.1" version="$Rev$"}% +*Related topics:* %SEARCH{"name='WikiUsers' OR name='WikiGroups' OR name='UserList' OR name~'UserListBy*'" type="query" web="%USERSWEB%" scope="topic" separator=", " nonoise="on" format="[[$web.$topic][$topic]]" excludetopic="%INCLUDINGTOPIC%"}% diff --git a/TopicUserMappingContrib/data/System/BulkRegistration.txt b/TopicUserMappingContrib/data/System/BulkRegistration.txt new file mode 100644 index 0000000000..3b9b3ee96d --- /dev/null +++ b/TopicUserMappingContrib/data/System/BulkRegistration.txt @@ -0,0 +1,105 @@ +%META:TOPICINFO{author="ProjectContributor" date="1111929255" format="1.0" version="$Rev: 16363 $"}% +%META:TOPICPARENT{name="ManagingUsers"}% +---+ Bulk Registration + +Administrators can use this topic to register (i.e. create logins and user topics) for a group of people in one batch. + +Unlike normal registration the administrator is assumed to have correct e-mail addresses for the users, so *no verification is required.* +Note that the new users are not notified that they have an account. This is so you can prepare and verify the accounts before announcing them. +To announce them use the [[BulkResetPassword]] feature: this will assign a new random password and notify users. + +---++ Bulk Registration usage +Note: this is an administrator job - only admistrators can run this. + +If you are administrator, you will take these actions: + 1 (First time use) Create new bulk registration topics (see [[#Settings][Settings]] below). + 1 In the REGISTERTOPIC topic: create a table of new users. An example table is provided below to copy. + 1 Return to this topic and press the button "Bulk Register" to create the new topics. + 1 Read %LOGTOPIC% to verify if all has gone well. + 1 When you are ready, use the [[BulkResetPassword]] page to assign passwords and notify the users of their new accounts. + +Below are the details. + +---++ Settings + * Define where to pick up the table of users to register + * Set REGISTERTOPIC = %USERSWEB%.UnprocessedRegistrations + * Use this to define where to log the bulk registration process. It needs + to be a topic name in this web. + * Set LOGTOPIC = %REGISTERTOPIC%Log + * Set this to 1 to make the bulk registration overwrite any existing user + topics. By default, existing user topics are left alone. + * Set OVERWRITEHOMETOPICS = 0 + +---++ The user table +This table is a template for user data that will be written to the new user topics. If you stick to these basic fields you can just use the first example below. If you want to write more data (like phone number or country) read the section [[#CustomizingUserData][Customizing user data]] as well. + +---+++ Example format +The following should be inserted into your %REGISTERTOPIC% as a table. This is the most simple format: + + +%EDITTABLE{}% +| FirstName | LastName | Email | WikiName | +| Test | User | you@example.com | TestUser | + + + +*Usage:* + 1 Copy this text to your clipboard + 1 Click through and paste this on %REGISTERTOPIC%. + 1 Add and customize entries, save table. Note that the first row must not contain bolded entries, so don't apply any formatting. + 1 Return here + +#CustomizingUserData +---+++ Customizing user data + +You can write additional data to the new user topics. Do this by enhancing the user table with additional field names as table headers. + +Any fields you define in this table will end up in the User's topic. If a form (such as %SYSTEMWEB%.UserForm) is attached to NewUserTemplate then the data will go in as META:FIELDS, meaning that you can use SEARCH formfield constructs to search. + +If you use the %SYSTEMWEB%.UserForm then ensure that it contains all the fields you define here. Otherwise they will disappear when the user edits their home topic! + +---++++ Mandatory fields + * !WikiName + * !FirstName + * !LastName + +---++++ Optional fields + * LoginName - if not set will register with WikiName + +---++++ Customized table example +Make sure that the extra fields also appear on the %SYSTEMWEB%.UserForm. + + +%EDITTABLE{}% +| FirstName | LastName | Email | WikiName | CustomFieldThis | SomeOtherRandomField | WhateverYouLike | +| Test | User | you@example.com | TestUser | A | B | C | + + + +--- +%IF{ + "context passwords_modifyable" + then="" + else="%MAKETEXT{"Sorry, the password system is currently read only, please contact [_1]" args="%WIKIWEBMASTER%"}%
" +}% + +
+  + + + +
+ +---++ %REGISTERTOPIC% + +%INCLUDE{"%REGISTERTOPIC%" warn="off"}% + + +---++ %LOGTOPIC% + +%INCLUDE{"%LOGTOPIC%" warn="off"}% + +--- +*Related Topics:* AdminToolsCategory diff --git a/TopicUserMappingContrib/data/System/BulkResetPassword.txt b/TopicUserMappingContrib/data/System/BulkResetPassword.txt new file mode 100644 index 0000000000..d8e5eef44b --- /dev/null +++ b/TopicUserMappingContrib/data/System/BulkResetPassword.txt @@ -0,0 +1,53 @@ +%META:TOPICINFO{author="ProjectContributor" date="1111929255" format="1.0" version="$Rev: 15703 $"}% +---+ Bulk Reset Passwords + +*Administrators can use this topic to reset any number of user passwords.* + +Users whose passwords are reset with this will be sent an e-mail at the address recorded *in their home topic*. The administrator will *not* be told the new passwords. + +*Follow these two steps:* + +
+
+
+---+++ Select users +%IF{ + "context passwords_modifyable" + then="" + else="%MAKETEXT{"Sorry, the password system is currently read only, please contact [_1]" args="%WIKIWEBMASTER%"}%" +}% + +%TABLE{databg="transparent" tableborder="0" tablerules="none"}% +%SEARCH{ + "form.name ~ '*.UserForm'" + web="%USERSWEB%" + type="query" + header="|*WikiName* |*User page created* |*User page changed* | *Reset* |" + format="| $web.$topic |$createdate |$date | |" + excludetopic="Web*" nosearch="on" nototal="on" +}% + +*Note* if you don't see all the users you expect in this table, make sure their home topic has an attached %SYSTEMWEB%.UserForm. This is used to identify users. +
+
+---+++ Write message + +*This text will be sent to users in their "reset" e-mail.* The login name and password will be filled in automatically. + +
+
+ + +
+
+
+ +--- +*Related Topics:* AdminToolsCategory + + * Set ALLOWTOPICVIEW = %USERSWEB%.AdminGroup + * Set ALLOWTOPICCHANGE = %USERSWEB%.AdminGroup diff --git a/TopicUserMappingContrib/data/System/ChangeEmailAddress.txt b/TopicUserMappingContrib/data/System/ChangeEmailAddress.txt new file mode 100644 index 0000000000..faf14856a3 --- /dev/null +++ b/TopicUserMappingContrib/data/System/ChangeEmailAddress.txt @@ -0,0 +1,53 @@ +%META:TOPICINFO{author="ProjectContributor" date="1111929255" format="1.0" version="$Rev: 8235 $"}% +---+ Change E-mail Address +%STARTINCLUDE% + +%MAKETEXT{"This form is used to change your registered e-mail addresses. Your registered e-mails are used by Foswiki for sending you e-mails, include notifications of password changes. The addresses you register via this form are kept secret and will *not* be published anywhere on this site."}% + +%X% *Security Note*: %MAKETEXT{"You really ought to register a valid e-mail address. If Foswiki can't find a registered e-mail for you in the secret database, it will look in your user topic for a line like this:"}% + + * Set Email = user@example.com + +%MAKETEXT{"If your user topic is not protected from changes by other people, and you don't register an e-mail address using this form, then your user account could be hijacked by someone else."}% + +%MAKETEXT{"If your old e-mail addresses are all invalid (you can't receive mail there any more) and you have forgotten your password, please contact [_1] for help." args="%WIKIWEBMASTER%"}% + +
+
+
+%IF{ + "context passwords_modifyable" + then="*%MAKETEXT{"After submitting this form your e-mail will be changed, and you will be returned to this form."}%*" + else="%MAKETEXT{"Sorry, the password system is currently read only, please contact [_1]" args="%WIKIWEBMASTER%"}%" +}% + +
+
+Registered e-mail addresses for currently logged in user (=%USERINFO{format="$wikiname"}%=): %USERINFO{format="$emails"}% +
+
+%TABLE{databg="transparent" tableborder="0" tablerules="none"}% +| | %MAKETEXT{"Fields marked [_1] are required" args="=**="}% | +| %MAKETEXT{"Your [_1].LoginName" args="%SYSTEMWEB%"}%: | =**= | +| %MAKETEXT{"Password"}%: | =**= | +| %MAKETEXT{"New e-mails (space-separated list)"}%: | =**= | +
+
+ + + +
+
+
+ +
+ * %ICON{info}% %MAKETEXT{"If you have any questions, please contact [_1]" args="%WIKIWEBMASTER%"}% + * %ICON{info}% %MAKETEXT{"[_1] has a list of other Foswiki users" args="%USERSWEB%.%WIKIUSERSTOPIC%"}% +
+ +%STOPINCLUDE% + +--- +*%MAKETEXT{"Related topics:"}%* ChangePassword, ResetPassword, UserToolsCategory, AdminToolsCategory diff --git a/TopicUserMappingContrib/data/System/ChangePassword.txt b/TopicUserMappingContrib/data/System/ChangePassword.txt new file mode 100644 index 0000000000..e15eaaed38 --- /dev/null +++ b/TopicUserMappingContrib/data/System/ChangePassword.txt @@ -0,0 +1,47 @@ +%META:TOPICINFO{author="ProjectContributor" date="1111929255" format="1.0" version="$Rev: 16822 $"}% +---+ %MAKETEXT{"Change Password"}% +%STARTINCLUDE% + +*%MAKETEXT{"Forgotten your password?"}%* %MAKETEXT{"Use [_1] instead." args="ResetPassword"}% + +
+
+
+%IF{ + "context passwords_modifyable" + then="%MAKETEXT{"After submitting this form your password will be changed."}%" + else="%MAKETEXT{"Sorry, the password system is currently read only, please contact [_1]" args="%WIKIWEBMASTER%"}%" +}% + +
+
+%TABLE{databg="transparent" tableborder="0" tablerules="none"}% +|%MAKETEXT{"Fields marked [_1] are required" args="=**="}% || +| %MAKETEXT{"Your [_1].LoginName" args="%SYSTEMWEB%"}%: | =**= | +| %MAKETEXT{"Current password"}%: | =**= | +| %MAKETEXT{"New password"}%: | =**= | +| %MAKETEXT{"Retype new password"}%: | =**= | +
+
+ + + +
+
+
+ +
+ * %ICON{info}% %MAKETEXT{"If you have any questions, please contact [_1]" args="%WIKIWEBMASTER%"}% + * %ICON{info}% %MAKETEXT{"[_1] has a list of other Foswiki users" args="%USERSWEB%.%WIKIUSERSTOPIC%"}% +
+ +%STOPINCLUDE% + +--- +*%MAKETEXT{"Related topics:"}%* ResetPassword, ChangeEmailAddress, UserToolsCategory, AdminToolsCategory + + diff --git a/TopicUserMappingContrib/data/System/LoginName.txt b/TopicUserMappingContrib/data/System/LoginName.txt new file mode 100644 index 0000000000..e6f9619bc4 --- /dev/null +++ b/TopicUserMappingContrib/data/System/LoginName.txt @@ -0,0 +1,26 @@ +%META:TOPICINFO{author="ProjectContributor" date="1158277691" format="1.0" version="$Rev: 15569 $"}% +%META:TOPICPARENT{name="ChangePassword"}% +---+ The name you log in with + +A login name is a sequence of alphanumeric characters and underscores. + +You cannot alter your !LoginName: you have to get the administrator to do this for you. + +---++ How to Find Yours + +Look in %USERSWEB%.WikiUsers - if your name is followed by a dash and a word, usually lowercase, this is what you log in with. + +e.g. + + * !WikiGuest - guest - 05 Sep 2004 + +Would mean that !WikiGuest would log in using the login name 'guest' + +If, on the other hand, if your entry is like this: + + * !WikiGuest - 05 Sep 2004 + +Then you would log in using your WikiName, in this case 'WikiGuest' + +--- +*Related Topics for Administrators:* ManagingUsers, UserDocumentationCategory diff --git a/TopicUserMappingContrib/data/System/ManagingUsers.txt b/TopicUserMappingContrib/data/System/ManagingUsers.txt new file mode 100644 index 0000000000..ba9413e3ac --- /dev/null +++ b/TopicUserMappingContrib/data/System/ManagingUsers.txt @@ -0,0 +1,156 @@ +%META:TOPICINFO{author="ProjectContributor" date="1158277691" format="1.1" version="$Rev: 17703 $"}% +%STARTINCLUDE% +---+ Manage Users + +_Register users on your Foswiki site; change/reset/install passwords; remove user accounts_ + +%TOC% + +%X% Some of the features below may be disabled, depending on your Foswiki +configuration. + +---++ Authentication and Access Control + + * UserAuthentication describes your options for user authentication + * AccessControl describes how to define groups and how to restrict + access to content + +---++ Register User + +You don't have to have user home pages in Foswiki for Authentication to +work - see UserAuthentication for details. + + * UserRegistration is used when you want new users to individually + register with Foswiki by filling out a form + * You can create a custom versions of %SYSTEMWEB%.NewUserTemplate and + %SYSTEMWEB%.UserForm + * BulkRegistration is used by administrators to register multiple users at + the same time + +---++ Change, Reset and Install Passwords + +_Note that the below features are only relevant when you use an internal password manager where Foswiki can set and reset passwords._ + + * ChangePassword is for users who _can_ remember their password and want to + change it + * ResetPassword is for users who _cannot_ remember their password; a system + generated password is e-mailed to them + * BulkResetPassword if for administrators who want to reset many passwords + at once + * ChangeEmailAddress changes the hidden email address stored in the password + file + +---++ Changing User Account Names + +To change the user's WikiName: + * Rename the user's Foswiki homepage in the %USERSWEB% web, such as + from =JaneSmith= to =JaneMiller=. + * Fix backlinks in the %USERSWEB% web only + * Make sure the group topics are updated (if any.) + * Edit the [[%USERSWEB%.WikiUsers]] topic and move the user's entry so + that the list is in proper alphabetical order. + * Recreate the old topic with a pointer to the new topic, so that links + in other webs work properly. Example content:%BR% + =%M% Jane Smith is now known as !JaneMiller= + +If external authentication is used and you want to change the login name: + * The login name needs to be changed in the authentication server (e.g. + Active Directory) + * In Foswiki's [[%USERSWEB%.WikiUsers]] topic, fix the mapping from + login name to WikiName:%BR% + =  * !JaneSmith - jsmith - 13 Sep 2006= %BR% + to: %BR% + =  * !JaneMiller - jmiller - 13 Sep 2006= + +---++ Removing User Accounts + +To remove a user account (FredQuimby, who logs in as "fred"): + + 1 If you are using a =.htpasswd= file, edit the =.htpasswd= file to + delete the line starting =fred:= + * *Warning:* Do *not* use the Apache =htpasswd= program with + =.htpasswd= files generated by Foswiki! =htpasswd= wipes out email + addresses that Foswiki plants in the info fields of this file. + 2 Remove the =FredQuimby - fred= line from the %USERSWEB%.WikiUsers + topic + 3 Remove =FredQuimby= from all groups and from all the + =ALLOWWEB/ALLOWTOPIC...= declarations, if any.%BR% *Note:* If you + fail to do this you risk creating a security hole, as the next user to + register with the wikiname FredQuimby will inherit the old + FredQuimby's permissions. + 4 _[optional]_ Delete their user topic %USERSWEB%.FredQuimby + (including attachments, if any.) + +*Note:* Consider leaving the user topic file in place so their past signatures and revision author entries don't end up looking like AnUncreatedTopic. +If you want to make it clear the user is no longer around, replace the topic content with a note to that effect. +The existence of the UserName topic should also prevent that user name from being re-used, sealing the potential security hole regarding inherited permissions.. + +---++ Customizing registration Emails. +Foswiki's Registration can send 3 emails who's output is governed by templates: + 1 User registration confirmation - =templates/registerconfirm.tmpl= + 2 User registration notification - =templates/registernotify.tmpl= + 3 Email to notify the Wiki admin of registration - =templates/registernotifyadmin.tmpl= + +As these are SkinTemplates, they can be customized and selected using the SKIN path setting. +Because there are default tmpl files in the =templates= dir, this cannot use Template topics. + +These template files have a specific format that matches the raw format of emails sent via SMTP, +so be careful and test your changes. It is easiest to start by copying the default templates: + + +cd templates +cp registernotify.tmpl registernotify.myskin.tmpl +cp registerconfirm.tmpl registerconfirm.myskin.tmpl +cp registernotifyadmin.tmpl registernotifyadmin.myskin.tmpl + +then add =myskin= to the beginning of the =SKIN= setting in %USERSWEB%.SitePreferences. + +From this point on, your myskin templates will be used for the registration emails. + +To make it possible for WikiUsers to modify the email contents, you could use a parameterized =%INCLUDE%= +statement in your customized version. eg: + + +From: %WIKIWEBMASTERNAME% <%WIKIWEBMASTER%> +To: %FIRSTLASTNAME% <%EMAILADDRESS%> +Subject: %MAKETEXT{"[_1] - Registration for [_2] ([_3])" args="%WIKITOOLNAME%, %WIKINAME%, %EMAILADDRESS%"}% +MIME-Version: 1.0 +Content-Type: text/plain; charset=%CHARSET% +Content-Transfer-Encoding: 8bit + +%INCLUDE{ + "Main.RegistrationNotification" + WIKINAME="%WIKINAME%" + FIRSTLASTNAME="%FIRSTLASTNAME%" + EMAILADDRESS="%EMAILADDRESS%" +}% + + * *note the use of %WIKINAME%, %FIRSTLASTNAME%, %EMAILADDRESS% passed in from the INCLUDE so that the topic below is similar to the original template* + +and then create a topic %NOP%%USERSWEB%.RegisterNotifyEmail: + +Welcome to %WIKITOOLNAME%. + +%MAKETEXT{"Your personal [_1] topic is located at [_2]. You can customize it as you like:" args="%WIKITOOLNAME%, %SCRIPTURL{"view"}%/%USERSWEB%/%WIKINAME%"}% + + * %MAKETEXT{"Some people turn it into a personal portal with favorite links, what they work on, what help they'd like, etc."}% + * %MAKETEXT{"Some add schedule information and vacation notice."}% + +Regards +%WIKIWEBMASTERNAME% +Your Wiki Admin + + +%MAKETEXT{"Note:"}% + 2 %MAKETEXT{"You can change your password at via [_1]" args="%SCRIPTURL{"view"}%/%SYSTEMWEB%/ChangePassword"}% + 3 %MAKETEXT{"If you haven't set a password yet or you want to reset it, go to: [_1]" args="%SCRIPTURL{"view"}%/%SYSTEMWEB%/ResetPassword"}% + +%MAKETEXT{"Submitted content:"}% +%FORMDATA% + +%META:PREFERENCE{name="ALLOWTOPICCHANGE" title="ALLOWTOPICCHANGE" type="Set" value="Main.AdminGroup"}% + + * *remember to secure the topic appropriately to prevent attackers from getting emailed sensitive passwords.* + +--- +*Related Topics:* AdminDocumentationCategory diff --git a/TopicUserMappingContrib/data/System/NewUserTemplate.txt b/TopicUserMappingContrib/data/System/NewUserTemplate.txt new file mode 100644 index 0000000000..bac2d54578 --- /dev/null +++ b/TopicUserMappingContrib/data/System/NewUserTemplate.txt @@ -0,0 +1,106 @@ +%META:TOPICINFO{author="ProjectContributor" date="1167346194" format="1.0" version="$Rev: 16686 $"}% +%SPLIT% + * %KEY%: %VALUE%%SPLIT% + +---++ My Links + + * [[%SYSTEMWEB%.BeginnersStartHere][BeginnersStartHere]] - view a short introductory presentation on Foswiki for beginners + * [[%SYSTEMWEB%.WelcomeGuest][WelcomeGuest]] - starting points on Foswiki + * [[%SYSTEMWEB%.UsersGuide][UsersGuide]] - complete Foswiki documentation, Quick Start to Reference + * [[Sandbox.%HOMETOPIC%][%HOMETOPIC%]] - try out Foswiki on your own + * [[Sandbox.%TOPIC%Sandbox][%TOPIC%Sandbox]] - just for me + * + * + +---++ My Personal Data + +Note: if personal data is being stored using a secret database, then it is only visible to the user and to administrators. + +| E-mail | %USERIN%NOP%FO{"%TOPIC%" format="$emails"}% | + +---++ My Personal Preferences + +Uncomment [[%SYSTEMWEB%.PreferenceSettings][preference settings]] to activate them (remove the #-sign). Help and details on preference settings are available in [[%SYSTEMWEB%.%WIKIPREFSTOPIC%][%WIKIPREFSTOPIC%]]. + + * Show tool-tip topic info on mouse-over of [[%SYSTEMWEB%.WikiWord][WikiWord]] links, on or off: + * #Set LINKTOOLTIPINFO = off + +---++ Related Topics + + * [[%SYSTEMWEB%.ChangePassword][ChangePassword]] for changing your password + * [[%SYSTEMWEB%.ChangeEmailAddress][ChangeEmailAddress]] for changing your email address + * [[%USERSWEB%.%WIKIUSERSTOPIC%][%WIKIUSERSTOPIC%]] has a list of other Foswiki users + * [[%SYSTEMWEB%.UserDocumentationCategory][UserDocumentationCategory]] is a list of Foswiki user documentation + * [[%SYSTEMWEB%.UserToolsCategory][UserToolsCategory]] lists all Foswiki user tools + +%STARTSECTION{type="templateonly"}% +---- +Notes to Wiki Administrator: + +Anything inside this "templateonly" section will not be copied to the home topic of the new user. It is a resource for the administrator to tailor the users home topic. + +On public Foswiki sites on the Internet user topics are often subject to spam so you may want to limit editing to the user himself. You can do this by moving these two lines to the Personal Preferences section above. Remove the small # in front of the "Set". + * Write protect your home page: (set it to your [[%SYSTEMWEB%.WikiName][WikiName]]) + * #Set ALLOWTOPICCHANGE = %WIKIUSERNAME% + +The default topic text above is for English speaking users. If you have a multilingual wiki community you can replace the "My Links", "Personal Preferences" and "Related Topics" sections above with the localized text below. Text enclosed in %MAKETEXT will be shown in the language selected by the user. Please note that the %MAKETEXT can be quite intimidating to new users. Consider translating above text to your own language if your community uses a non-English language. (Remove all text from =%STARTSECTION{type="templateonly"}%= to =%ENDSECTION{type="templateonly"}%= when you are done) + +Text for multilingual wiki community, copy from here to %ENDSECTION{type="templateonly"}% + + +---++ %MAKETEXT{"My Links"}% + + * %MAKETEXT{"[_1] - view a short introductory presentation on Foswiki for beginners" args="[[%SYSTEMWEB%.BeginnersStartHere][BeginnersStartHere]]"}% + * %MAKETEXT{"[_1] - starting points on Foswiki" args="[[%SYSTEMWEB%.WelcomeGuest][WelcomeGuest]]"}% + * %MAKETEXT{"[_1] - complete Foswiki documentation, Quick Start to Reference" args="[[%SYSTEMWEB%.UsersGuide][UsersGuide]]"}% + * %MAKETEXT{"[_1] - try out Foswiki on your own" args="[[Sandbox.%HOMETOPIC%][%HOMETOPIC%]]"}% + * %MAKETEXT{"[_1] - just for me" args="[[Sandbox.%TOPIC%Sandbox][%TOPIC%Sandbox]]"}% + * + * + +---++ %MAKETEXT{"Personal Preferences"}% + +%MAKETEXT{"Uncomment preference settings to activate them (remove the #-sign). Help and details on preference settings are available in [_1]." args="[[%SYSTEMWEB%.%WIKIPREFSTOPIC%][%WIKIPREFSTOPIC%]]"}% + + * %MAKETEXT{"Show tool-tip topic info on mouse-over of [_1] links, on or off:" args="[[%SYSTEMWEB%.WikiWord][WikiWord]]"}% + * #Set LINKTOOLTIPINFO = off + +---++ %MAKETEXT{"Related Topics"}% + + * %MAKETEXT{"[_1] for changing your password" args="[[%SYSTEMWEB%.ChangePassword][ChangePassword]]"}% + * %MAKETEXT{"[_1] for changing your email address" args="[[%SYSTEMWEB%.ChangeEmailAddress][ChangeEmailAddress]]"}% + * %MAKETEXT{"[_1] has a list of other Foswiki users" args="[[%USERSWEB%.%WIKIUSERSTOPIC%][%WIKIUSERSTOPIC%]]"}% + * %MAKETEXT{"[_1] is a list of Foswiki user documentation" args="[[%SYSTEMWEB%.UserDocumentationCategory][UserDocumentationCategory]]"}% + * %MAKETEXT{"[_1] lists all Foswiki user tools" args="[[%SYSTEMWEB%.UserToolsCategory][UserToolsCategory]]"}% + +%ENDSECTION{type="templateonly"}% +%META:FORM{name="%SYSTEMWEB%.UserForm"}% +%META:FIELD{name="FirstName" attributes="" title="FirstName" value=""}% +%META:FIELD{name="LastName" attributes="" title="LastName" value=""}% +%META:FIELD{name="OrganisationName" attributes="" title="OrganisationName" value=""}% +%META:FIELD{name="OrganisationURL" attributes="" title="OrganisationURL" value=""}% +%META:FIELD{name="Profession" attributes="" title="Profession" value=""}% +%META:FIELD{name="Country" attributes="" title="Country" value=""}% +%META:FIELD{name="State" attributes="" title="State" value=""}% +%META:FIELD{name="Address" attributes="" title="Address" value=""}% +%META:FIELD{name="Location" attributes="" title="Location" value=""}% +%META:FIELD{name="Telephone" attributes="" title="Telephone" value=""}% +%META:FIELD{name="VoIP" attributes="" title="VoIP" value=""}% +%META:FIELD{name="InstantMessagingIM" attributes="" title="InstantMessaging (IM)" value=""}% +%META:FIELD{name="Email" attributes="" title="Email" value=""}% +%META:FIELD{name="HomePage" attributes="" title="HomePage" value=""}% +%META:FIELD{name="Comment" attributes="" title="Comment" value=""}% +%META:PREFERENCE{name="VIEW_TEMPLATE" title="VIEW_TEMPLATE" type="Local" value="UserView"}% diff --git a/TopicUserMappingContrib/data/System/ResetPassword.txt b/TopicUserMappingContrib/data/System/ResetPassword.txt new file mode 100644 index 0000000000..18161f1292 --- /dev/null +++ b/TopicUserMappingContrib/data/System/ResetPassword.txt @@ -0,0 +1,45 @@ +%META:TOPICINFO{author="ProjectContributor" date="1111929255" format="1.0" version="$Rev: 15703 $"}% +%META:TOPICPARENT{name="ChangePassword"}% +---+ %MAKETEXT{"Reset Password"}% +%STARTINCLUDE% +*%MAKETEXT{"Remember your password?"}%* %MAKETEXT{"Use [_1] instead." args="ChangePassword"}% %MAKETEXT{"Otherwise, use this form to get a new one e-mailed to you."}% + + + +
+
+
+ +%IF{ + "context passwords_modifyable" + then="%MAKETEXT{"After submitting this form, you will receive an e-mail with your new, *system-generated* password, and a link to a page where you can change it."}%" + else="%MAKETEXT{"Sorry, the password system is currently read only, please contact [_1]" args="%WIKIWEBMASTER%"}%" +}% + + +%X% %MAKETEXT{"you *must* have at least one valid registered e-mail to be able to reset your password. If none of your registered e-mail addresses is valid, and you have forgotten your password, contact [_1]." args="%WIKIWEBMASTER%"}% +
+
+%TABLE{databg="transparent" tableborder="0" tablerules="none"}% +|%MAKETEXT{"Fields marked [_1] are required" args="=**="}% || +| %MAKETEXT{"Your [_1].LoginName" args="%SYSTEMWEB%"}%: | =**= | +
+
+ + + +
+
+
+ +
+ * %ICON{info}% %MAKETEXT{"[_1] has a list of other Foswiki users" args="%USERSWEB%.%WIKIUSERSTOPIC%"}% + * %ICON{info}% %MAKETEXT{"If you have any questions, please contact [_1]" args="%WIKIWEBMASTER%"}% +
+%STOPINCLUDE% + +--- +*%MAKETEXT{"Related topics:"}%* ChangePassword, ChangeEmailAddress, UserToolsCategory, AdminToolsCategory + diff --git a/TopicUserMappingContrib/data/System/TopicUserMappingContrib.txt b/TopicUserMappingContrib/data/System/TopicUserMappingContrib.txt new file mode 100644 index 0000000000..c1b5d0caed --- /dev/null +++ b/TopicUserMappingContrib/data/System/TopicUserMappingContrib.txt @@ -0,0 +1,79 @@ +---+!! !TopicUserMappingContrib + +%SHORTDESCRIPTION% + +%TOC% + +The _User Mapping Manager_ interface provides services for mapping between a 'user identity' as used when talking to an external authentication provider, +an internal canonical user ID, and the displayed name used to identify the user in topics. +This is the default TopicUserMapping in which user information is stored in topics - one per user, and then any mapping needed is done using the +WikiUsers topic in the %USERSWEB%. + +See [[http://twiki.org/cgi-bin/view/Codev/SimplifiedUserMappingCodeInterface][SimplifiedUserMappingCodeInterface]] for a more detailed discussion +of the concepts. + +It includes the topics that are used when registering and managing users and groups via Foswiki: + * Registration and user management topics + * %SYSTEMWEB%.UserRegistration + * %SYSTEMWEB%.ResetPassword + * %SYSTEMWEB%.ChangePassword + * %SYSTEMWEB%.ChangeEmailAddress + * %SYSTEMWEB%.BulkRegistration + * %SYSTEMWEB%.BulkResetPassword + * Documentation topics + * %SYSTEMWEB%.ManagingUsers + * %SYSTEMWEB%.UserToolsCategory + * %SYSTEMWEB%.LoginName + * default groups and user + * %USERSWEB%.NobodyGroup + * %USERSWEB%.AdminGroup + * %USERSWEB%.RegistrationAgent + * User lists + * %USERSWEB%.UserList + * %USERSWEB%.UserListHeader + * %USERSWEB%.UserListByDateJoined + * %USERSWEB%.UserListByLocation + * %USERSWEB%.UserListByPhotograph + * User and Group template files + * %SYSTEMWEB%.NewUserTemplate + * %SYSTEMWEB%.UserForm + * %SYSTEMWEB%.UsersTemplate + * %USERSWEB%.UserHomepageHeader + * %SYSTEMWEB%.UserSetting + * %USERSWEB%.GroupTemplate + +If you are developing a non-topic based User mapper, you might like to consider removeing or replacing the files installed by this Contrib. + +---++ Adding and deleting users +Users are added using the default registration process in Foswiki. To delete a user (admins only), + + 1. Edit the .htpasswd file to delete their entry (see =configure= for the location + 2. Remove their topic files: data/%USERSWEB%/FredQuimby.txt and data/%USERSWEB%/FredQuimby.txt,v + 3. Remove the FredQuimby line from the %USERSWEB%.WikiUsers topic + 4. Remove user from all groups and from all the ALLOWWEB/ALLOWTOPIC... declarations, if any. + Note: Otherwise this will leave a security hole, as the first person to re-register with this name will be granted the permissions of the previous user. + +---++ Settings +The !TopicUserMappingContrib specific settings have not yet been moved out of the standard Foswiki.spec. This will be done in a future release. + +---++ Installation Instructions + +It's a part of the default distribution of the Foswiki Core. + +---++ Contrib Info + +| Contrib Author(s): | Copyright (C) 2007-2008 Sven Dowideit, SvenDowideit@WikiRing.com and Project Contributors. | +| License: | [[http://www.gnu.org/licenses/gpl.html][GPL (Gnu General Public License)]] | +| Contrib Version: | 4.2.2 (16 Dec 2008) | +| Change History: |   | +| 4.2.2 | 16 Dec 2008 - Foswiki version | +| 4.2.1 | 03 Aug 2008 - Distributed with TWiki 4.2.1 | +| 4.2.0 | 22 Jan 2008 - Distributed with TWiki 4.2.0 | +| Dependencies: | %$DEPENDENCIES% | +| Contrib Home: | http://foswiki.org/Extensions/%TOPIC% | +| Feedback: | http://foswiki.org/Extensions/%TOPIC%Dev | + + diff --git a/TopicUserMappingContrib/data/System/UserForm.txt b/TopicUserMappingContrib/data/System/UserForm.txt new file mode 100644 index 0000000000..b1be234806 --- /dev/null +++ b/TopicUserMappingContrib/data/System/UserForm.txt @@ -0,0 +1,56 @@ +%META:TOPICINFO{author="ProjectContributor" date="1111929255" format="1.0" version="$Rev: 15569 $"}% +---+ User Form + +This is a maintenance topic, used by the Wiki administrator. + +---++ Data form definition of user topics + +|*Name* |*Type* |*Size* |*Values* |*Tooltip message* |*Attributes* | +| FirstName | text | 40 | | | | +| LastName | text | 40 | | | | +| OrganisationName | text | 40 | | | | +| OrganisationURL | text | 40 | | | | +| Profession | text | 40 | | | | +| Country | text | 40 | | | | +| State | text | 40 | | | | +| Address | text | 40 | | | | +| Location | text | 40 | | | | +| Telephone | text | 40 | | | | +| VoIP | text | 40 | | | | +| InstantMessaging (IM) | textarea | 50x4 | | (One account per line, if several) | | +| Email | text | 40 | | | | +| HomePage | text | 40 | | | | +| Comment | textarea | 50x6 | | | | + +#CustomForm +---++ Customizing the User Form +If you want to change the user form, you would first want to make a copy of this form to the %USERSWEB% Web. That ensures your changes stay intact after a Foswiki update. + +To make a copy, follow these steps: + + * By hand: + 1 Take a copy of this topic and save it to %IF{"istopic '%USERSWEB%.UserForm'" then="[[%USERSWEB%.UserForm]]" else="%USERSWEB%.UserForm"}% + 1 When editing your new !UserForm topic, change the entries in the data form definition. You may remove all explanation around the table. + * Automatically: %BR% +
%BR% + you will return here after saving + +%X% Note that your form changes will only be visible in a user topic after it has been saved. %BR% +%X% Please keep the %SYSTEMWEB%.UserRegistration form and this form definition in sync. When you update this form you need to edit/save the %SYSTEMWEB%.NewUserTemplate topic. This ensures that the fields are in the right order. + +---+++ A form for new users +To use your new user form for new (registering) users, create a copy of %IF{"istopic '%USERSWEB%.NewUserTemplate'" then="[[%USERSWEB%.NewUserTemplate]]" else="%USERSWEB%.NewUserTemplate"}%. + + * By hand: + 1 Take a copy of [[%SYSTEMWEB%.NewUserTemplate]] and save it to %IF{"istopic '%USERSWEB%.NewUserTemplate'" then="[[%USERSWEB%.NewUserTemplate]]" else="%USERSWEB%.NewUserTemplate"}% + 1 Set the topic form to your custom %IF{"istopic '%USERSWEB%.UserForm'" then="%USERSWEB%.UserForm" else="%USERSWEB%.UserForm"}% topic + + * Automatically: %BR% +
%BR% + you will return here after saving + +--- +*Related topics:* %USERSWEB%.%WIKIUSERSTOPIC%, %USERSWEB%.UserViewTemplate, %SYSTEMWEB%.NewUserTemplate, %USERSWEB%.UserHomepageHeader, %SYSTEMWEB%.DataForms, %SYSTEMWEB%.UserRegistration + diff --git a/TopicUserMappingContrib/data/System/UserRegistration.txt b/TopicUserMappingContrib/data/System/UserRegistration.txt new file mode 100644 index 0000000000..2aac79af7e --- /dev/null +++ b/TopicUserMappingContrib/data/System/UserRegistration.txt @@ -0,0 +1,305 @@ +%META:TOPICINFO{author="ProjectContributor" date="1216489724" format="1.0" version="$Rev: 17064 $"}% +---+ %MAKETEXT{"Registration"}% + +%IF{"context registration_supported" +then="*%MAKETEXT{"To edit pages on this Foswiki site, you must have a registered user name and password."}%* %IF{"context registration_enabled" then="" else="%BR%%BR% *%MAKETEXT{"Sorry, Registration has been temporarily disabled"}%* "}%" +else="*%MAKETEXT{"This Foswiki does _not_ support new User Registration"}%*" +}% + + + +%H% *%MAKETEXT{"Note:"}%* %MAKETEXT{"Registered users can [[[_1]][change]] and [[[_2]][reset]] their passwords." args="%SYSTEMWEB%.ChangePassword,%SYSTEMWEB%.ResetPassword"}% + +%MAKETEXT{"*Important:* the information provided in this form will be stored in a database on the Foswiki server. This database is accessible to anyone who can access the server through the web (though passwords will be encrypted, and e-mail addresses will be obfusticated to help prevent spamming). Your country, or the country where the server is hosted, may have Data Protection laws governing the maintenance of such databases. If you are in doubt, you should contact [_1] for details of the Data Protection Policy of this Foswiki server before registering." args="%WIKIWEBMASTER%"}% + + + + + +
+
+
+*%MAKETEXT{"To register as a new user, simply fill out this form:"}%* +
+
+ + + + + + + + + + + + + + + + + +%IF{"$ ALLOWLOGINNAME" then="%BESPOKE_AUTH%"}% + + + + + +%IF{"$ ALLOWLOGINNAME < 1" then="%BESPOKE_PASSWORD%"}% + + + + + + + + + + + + + + +
%MAKETEXT{"Fields marked [_1] are required" args="=**="}%
%MAKETEXT{"First Name:"}% =**=
%MAKETEXT{"Last Name:"}% =**=
%MAKETEXT{"(identifies you to others) WikiName:"}% =**=
%MAKETEXT{"E-mail address:"}% =**=
%MAKETEXT{"Organisation name:"}%
%MAKETEXT{"Organisation URL:"}%
%MAKETEXT{"Country:"}% + =**= +
%MAKETEXT{"Comments:"}%
%MAKETEXT{"(optional)"}%
+
+
+%MAKETEXT{"When [_1] receives the form, it will mail an account activation code to the e-mail address you gave above. Enter that activation code in the following screen, or follow the link in the e-mail, to activate your account. Once your account is activated, [_1] will:" args="%WIKITOOLNAME%"}% + * %MAKETEXT{"Finish creating an account for you, so that you can start editing pages using your WikiName."}% + * %MAKETEXT{"Create your personal [_1] topic using your WikiName as topic name, and add the data you submitted to the topic." args="%WIKITOOLNAME%"}% + * %MAKETEXT{"Add your name to the list of users in the [_1] topic in the [_2] web." args="%USERSWEB%.%WIKIUSERSTOPIC%,%USERSWEB%"}% + * %MAKETEXT{"Send you a confirmation of your registration by e-mail."}% + +%MAKETEXT{"Once registered you can login using your [_1] and password." args="%LOGIN_METHOD_IN_THIS_SITE%"}% +
+
+ + + + +
+
+
+ +
+ * %ICON{info}% %MAKETEXT{"If you have any questions about registration, send an e-mail to [_1]." args="%WIKIWEBMASTER%"}% +
+ diff --git a/TopicUserMappingContrib/data/System/UserSetting.txt b/TopicUserMappingContrib/data/System/UserSetting.txt new file mode 100644 index 0000000000..903571d34a --- /dev/null +++ b/TopicUserMappingContrib/data/System/UserSetting.txt @@ -0,0 +1,19 @@ +%META:TOPICINFO{author="ProjectContributor" date="1187649998" format="1.1" version="1.2"}% +%META:TOPICPARENT{name="DefaultPreferences"}% +---+++ User Settings -- preference settings customizable by users +Some of the preference settings are explicitly intended to be customized by users, though a default is provided in [[%SYSTEMWEB%.%WIKIPREFSTOPIC%]] and site wide customisation can take place in [[%LOCALSITEPREFS%]]. + +User settings are customised by asetting a value in the users' personal topic. +For example, +Example: + + * Set LINKTOOLTIPINFO = off + + +This value will apply for the user when they are logged in, but will not +affect other users. + +*See also:* + * [[%SYSTEMWEB%.%WIKIPREFSTOPIC%]] - default preference settings + * [[%LOCALSITEPREFS%]] - preference settings local to this site + * [[%SYSTEMWEB%.Macros][Macros]] - an alphabetical list of all macros diff --git a/TopicUserMappingContrib/data/System/UserToolsCategory.txt b/TopicUserMappingContrib/data/System/UserToolsCategory.txt new file mode 100644 index 0000000000..36cb539499 --- /dev/null +++ b/TopicUserMappingContrib/data/System/UserToolsCategory.txt @@ -0,0 +1,4 @@ +%META:TOPICINFO{author="ProjectContributor" date="1122409431" format="1.1" version="$Rev $"}% +---+ A List of User Tools + +%SEARCH{"%TOPIC%" excludetopic="%TOPIC%" nonoise="on" format=" * $web.$topic$n * $summary(100, noheader)"}% diff --git a/TopicUserMappingContrib/data/System/UsersTemplate.txt b/TopicUserMappingContrib/data/System/UsersTemplate.txt new file mode 100644 index 0000000000..ff0cd51919 --- /dev/null +++ b/TopicUserMappingContrib/data/System/UsersTemplate.txt @@ -0,0 +1,53 @@ +%META:TOPICINFO{author="AdminGroup" date="1178574333" format="1.1" reprev="1.2" version="1.2"}% +---+ List of %WIKITOOLNAME% users + +Below is a list of users with accounts. If you want to edit topics or see protected areas of the site then you can get added to the list by registering: fill out the form in %SYSTEMWEB%.UserRegistration. + +If you forget your password, %SYSTEMWEB%.ResetPassword will get a new one sent to you. + +%INCLUDE{"UserListHeader"}% + +---- +#ListStart +[[#A][A]] [[#B][B]] [[#C][C]] [[#D][D]] [[#E][E]] [[#F][F]] [[#G][G]] [[#H][H]] [[#I][I]] [[#J][J]] [[#K][K]] [[#L][L]] [[#M][M]] [[#N][N]] [[#O][O]] [[#P][P]] [[#Q][Q]] [[#R][R]] [[#S][S]] [[#T][T]] [[#U][U]] [[#V][V]] [[#W][W]] [[#X][X]] [[#Y][Y]] [[#Z][Z]] + + * A - - - - - + * B - - - - - + * C - - - - - + * D - - - - - + * E - - - - - + * F - - - - - + * G - - - - - + * H - - - - - + * I - - - - - + * J - - - - - + * K - - - - - + * L - - - - - + * M - - - - - + * N - - - - - + * O - - - - - + * P - - - - - + * Q - - - - - + * R - - - - - + * S - - - - - + * T - - - - - + * ProjectContributor - 1 Jan 2005 + * WikiGuest - guest - 10 Feb 1999 + * RegistrationAgent - 1 Jan 2005 + * U - - - - - + * UnknownUser - 1 Jan 2005 + * V - - - - - + * W - - - - - + * X - - - - - + * Y - - - - - + * Z - - - - - + +*%X% Note:* There are four default system users: + * *ProjectContributor* - placeholder for a Foswiki developer, and is used in documentation + * *WikiGuest* - guest user, used as a fallback if the user can't be identified + * *RegistrationAgent* - special user used during the new user registration process + * *UnknownUser* - used where the author of a previously stored piece of data can't be determined + +--- +*Access Control:* + * Set ALLOWTOPICCHANGE = AdminGroup diff --git a/TopicUserMappingContrib/lib/Foswiki/Contrib/TopicUserMappingContrib.pm b/TopicUserMappingContrib/lib/Foswiki/Contrib/TopicUserMappingContrib.pm new file mode 100644 index 0000000000..3d47dede72 --- /dev/null +++ b/TopicUserMappingContrib/lib/Foswiki/Contrib/TopicUserMappingContrib.pm @@ -0,0 +1,23 @@ +# Contrib for Foswiki - The Free and Open Source Wiki, http://foswiki.org/ +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details, published at +# http://www.gnu.org/copyleft/gpl.html + +package Foswiki::Contrib::TopicUserMappingContrib; + +use strict; + +use vars qw( $VERSION $RELEASE $SHORTDESCRIPTION ); + +$VERSION = '$Rev$'; +$RELEASE = ''; +$SHORTDESCRIPTION = ''; + diff --git a/TopicUserMappingContrib/lib/Foswiki/Contrib/TopicUserMappingContrib/Config.spec b/TopicUserMappingContrib/lib/Foswiki/Contrib/TopicUserMappingContrib/Config.spec new file mode 100644 index 0000000000..5a9bdb03c1 --- /dev/null +++ b/TopicUserMappingContrib/lib/Foswiki/Contrib/TopicUserMappingContrib/Config.spec @@ -0,0 +1,20 @@ + +#---++ Registration +# **BOOLEAN** +# by turning this option off, you can temporarily disable new user registration. +# it will have no effect on existing users. +$Foswiki::cfg{Register}{EnableNewUserRegistration} = $TRUE; + +# **BOOLEAN EXPERT** +# Hide password in registration email to the *user* +# Note that Foswiki sends admins a separate confirmation. +$Foswiki::cfg{Register}{HidePasswd} = $TRUE; + +# **BOOLEAN** +# Whether registrations must be verified by the user following +# a link sent in an email to the user's registered email address +$Foswiki::cfg{Register}{NeedVerification} = $FALSE; + +# **STRING 20 EXPERT** +# The internal user that creates user topics on new registrations. You are recommended not to change this. +$Foswiki::cfg{Register}{RegistrationAgentWikiName} = 'RegistrationAgent'; \ No newline at end of file diff --git a/TopicUserMappingContrib/lib/Foswiki/Contrib/TopicUserMappingContrib/DEPENDENCIES b/TopicUserMappingContrib/lib/Foswiki/Contrib/TopicUserMappingContrib/DEPENDENCIES new file mode 100644 index 0000000000..154651c35e --- /dev/null +++ b/TopicUserMappingContrib/lib/Foswiki/Contrib/TopicUserMappingContrib/DEPENDENCIES @@ -0,0 +1,5 @@ +# Dependencies for TopicUserMappingContrib +# Example: +# Time::ParseDate,>=2003.0211,cpan,Required. +# Foswiki::Plugins,>=1.15,perl,Foswiki 1 release. + diff --git a/TopicUserMappingContrib/lib/Foswiki/Contrib/TopicUserMappingContrib/MANIFEST b/TopicUserMappingContrib/lib/Foswiki/Contrib/TopicUserMappingContrib/MANIFEST new file mode 100644 index 0000000000..e333011b99 --- /dev/null +++ b/TopicUserMappingContrib/lib/Foswiki/Contrib/TopicUserMappingContrib/MANIFEST @@ -0,0 +1,33 @@ +# Release manifest for TopicUserMappingContrib +data/System/TopicUserMappingContrib.txt 0644 Documentation +lib/Foswiki/Contrib/TopicUserMappingContrib.pm 0444 Perl module +lib/Foswiki/Users/TopicUserMapping.pm 0444 Perl module +lib/Foswiki/Contrib/TopicUserMappingContrib/Config.spec + +# Topic based Usermapping topics +data/Main/RegistrationAgent.txt 0644 +data/Main/UserListHeader.txt 0644 +data/Main/NobodyGroup.txt 0644 +data/Main/UserList.txt 0644 +data/Main/UserListByDateJoined.txt 0644 +data/Main/AdminGroup.txt 0644 Wiki compatible admin group +data/Main/AdminGroup.txt 0644 +data/Main/UserHomepageHeader.txt 0644 +data/Main/UserListByLocation.txt 0644 +data/Main/GroupTemplate.txt 0644 + +data/System/UserRegistration.txt 0644 +data/System/UsersTemplate.txt 0644 Default template for a new WikiUsers topic +data/System/ChangeEmailAddress.txt 0644 +data/System/BulkResetPassword.txt 0644 +data/System/UserSetting.txt 0644 +data/System/UserForm.txt 0644 Default form attached to a user topic +data/System/NewUserTemplate.txt 0644 Default new user topic template +data/System/ResetPassword.txt 0644 +data/System/ChangePassword.txt 0644 +data/System/BulkRegistration.txt 0644 +data/System/LoginName.txt 0644 +data/System/UserToolsCategory.txt 0644 +data/System/ManagingUsers.txt 0644 + +tools/upgrade_emails.pl 0444 Upgrade emails in topics to password database diff --git a/TopicUserMappingContrib/lib/Foswiki/Contrib/TopicUserMappingContrib/build.pl b/TopicUserMappingContrib/lib/Foswiki/Contrib/TopicUserMappingContrib/build.pl new file mode 100755 index 0000000000..030a305744 --- /dev/null +++ b/TopicUserMappingContrib/lib/Foswiki/Contrib/TopicUserMappingContrib/build.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl -w +BEGIN { + unshift @INC, split( /:/, $ENV{FOSWIKI_LIBS} ); +} +use Foswiki::Contrib::Build; + +# Create the build object +$build = new Foswiki::Contrib::Build('TopicUserMappingContrib'); + +# (Optional) Set the details of the repository for uploads. +# This can be any web on any accessible Wiki installation. +# These defaults will be used when expanding tokens in .txt +# files, but be warned, they can be overridden at upload time! + +# name of web to upload to +$build->{UPLOADTARGETWEB} = 'Extensions'; +# Full URL of pub directory +$build->{UPLOADTARGETPUB} = 'http://foswiki.org/pub'; +# Full URL of bin directory +$build->{UPLOADTARGETSCRIPT} = 'http://foswiki.org/bin'; +# Script extension +$build->{UPLOADTARGETSUFFIX} = ''; + +# Build the target on the command line, or the default target +$build->build($build->{target}); + diff --git a/TopicUserMappingContrib/lib/Foswiki/Users/TopicUserMapping.pm b/TopicUserMappingContrib/lib/Foswiki/Users/TopicUserMapping.pm new file mode 100755 index 0000000000..2a1b16ff8c --- /dev/null +++ b/TopicUserMappingContrib/lib/Foswiki/Users/TopicUserMapping.pm @@ -0,0 +1,1184 @@ +# Module of Foswiki - The Free and Open Source Wiki, http://foswiki.org/ +# +# Copyright (C) 2008-2009 Foswiki Contributors. Foswiki Contributors +# are listed in the AUTHORS file in the root of this distribution. +# NOTE: Please extend that file, not this notice. +# +# Additional copyrights apply to some or all of the code in this file: +# +# Copyright (C) 2007-2008 Sven Dowideit, SvenDowideit@distributedINFORMATION.com +# and TWiki Contributors. All Rights Reserved. Foswiki Contributors +# are listed in the AUTHORS file in the root of this distribution. +# NOTE: Please extend that file, not this notice. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. For +# more details read LICENSE in the root of this distribution. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# As per the GPL, removal of this notice is prohibited. + +=begin TML + +---+ package Foswiki::Users::TopicUserMapping + +The User mapping is the process by which Foswiki maps from a username (a login name) +to a wikiname and back. It is also where groups are defined. + +By default Foswiki maintains user topics and group topics in the %MAINWEB% that +define users and group. These topics are + * !WikiUsers - stores a mapping from usernames to Wiki names + * !WikiName - for each user, stores info about the user + * !GroupNameGroup - for each group, a topic ending with "Group" stores a list of users who are part of that group. + +Many sites will want to override this behaviour, for example to get users and groups from a corporate database. + +This class implements the basic Foswiki behaviour using topics to store users, +but is also designed to be subclassed so that other services can be used. + +Subclasses should be named 'XxxxUserMapping' so that configure can find them. + +=cut + +package Foswiki::Users::TopicUserMapping; +use base 'Foswiki::UserMapping'; + +use strict; +use Assert; +use Error qw( :try ); + +#use Monitor; +#Monitor::MonitorMethod('Foswiki::Users::TopicUserMapping'); + +=begin TML + +---++ ClassMethod new ($session, $impl) + +Constructs a new user mapping handler of this type, referring to $session +for any required Foswiki services. + +=cut + +# The null mapping name is reserved for Foswiki for backward-compatibility. +# We declare this as a global variable so we can override it during testing. +our $FOSWIKI_USER_MAPPING_ID = ''; + +#our $FOSWIKI_USER_MAPPING_ID = 'TestMapping_'; + +sub new { + my ( $class, $session ) = @_; + + my $this = $class->SUPER::new( $session, $FOSWIKI_USER_MAPPING_ID ); + + my $implPasswordManager = $Foswiki::cfg{PasswordManager}; + $implPasswordManager = 'Foswiki::Users::Password' + if ( $implPasswordManager eq 'none' ); + eval "require $implPasswordManager"; + die $@ if $@; + $this->{passwords} = $implPasswordManager->new($session); + +#if password manager says sorry, we're read only today +#'none' is a special case, as it means we're not actually using the password manager for +# registration. + if ( $this->{passwords}->readOnly() + && ( $Foswiki::cfg{PasswordManager} ne 'none' ) ) + { + $session->logger->log('warning', +'TopicUserMapping has TURNED OFF EnableNewUserRegistration, because the password file is read only.' + ); + $Foswiki::cfg{Register}{EnableNewUserRegistration} = 0; + } + + #SMELL: and this is a second user object + #TODO: combine with the one in Foswiki::Users + #$this->{U2L} = {}; + $this->{L2U} = {}; + $this->{U2W} = {}; + $this->{W2U} = {}; + $this->{eachGroupMember} = {}; + + return $this; +} + +=begin TML + +---++ ObjectMethod finish() +Break circular references. + +=cut + +# Note to developers; please undef *all* fields in the object explicitly, +# whether they are references or not. That way this method is "golden +# documentation" of the live fields in the object. +sub finish { + my $this = shift; + + $this->{passwords}->finish() if $this->{passwords}; + undef $this->{L2U}; + undef $this->{U2W}; + undef $this->{W2U}; + undef $this->{passwords}; + undef $this->{eachGroupMember}; + $this->SUPER::finish(); +} + +=begin TML + +---++ ObjectMethod supportsRegistration () -> false +return 1 if the UserMapper supports registration (ie can create new users) + +=cut + +sub supportsRegistration { + return 1; +} + +=begin TML + +---++ ObjectMethod handlesUser ( $cUID, $login, $wikiname) -> $boolean + +Called by the Foswiki::Users object to determine which loaded mapping +to use for a given user. + +The user can be identified by any of $cUID, $login or $wikiname. Any of +these parameters may be undef, and they should be tested in order; cUID +first, then login, then wikiname. This mapping is special - for backwards +compatibility, it assumes responsibility for _all_ non BaseMapping users. +If you're needing to mix the TopicUserMapping with other mappings, +define $this->{mapping_id} = 'TopicUserMapping_'; + +=cut + +sub handlesUser { + my ( $this, $cUID, $login, $wikiname ) = @_; + + if ( defined $cUID && !length( $this->{mapping_id} ) ) { + + # Handle all cUIDs if the mapping ID is not defined + return 1; + } + else { + + # Used when (if) TopicUserMapping is subclassed + return 1 if ( defined $cUID && $cUID =~ /^($this->{mapping_id})/ ); + } + + # Check the login id to see if we know it + return 1 if ( $login && $this->_userReallyExists($login) ); + + # Or the wiki name + if ($wikiname) { + _loadMapping($this); # Sorry Sven, has to be done + return 1 if defined $this->{W2U}->{$wikiname}; + } + + return 0; +} + +=begin TML + +---++ ObjectMethod login2cUID ($login, $dontcheck) -> $cUID + +Convert a login name to the corresponding canonical user name. The +canonical name can be any string of 7-bit alphanumeric and underscore +characters, and must correspond 1:1 to the login name. +(undef on failure) + +(if dontcheck is true, return a cUID for a nonexistant user too. +This is used for registration) + +=cut + +sub login2cUID { + my ( $this, $login, $dontcheck ) = @_; + + unless ($dontcheck) { + return undef unless ( _userReallyExists( $this, $login ) ); + } + + return $this->{mapping_id} . Foswiki::Users::mapLogin2cUID($login); +} + +=begin TML + +---++ ObjectMethod getLoginName ($cUID) -> login + +Converts an internal cUID to that user's login +(undef on failure) + +=cut + +sub getLoginName { + my ( $this, $cUID ) = @_; + ASSERT($cUID) if DEBUG; + + my $login = $cUID; + + #can't call userExists - its recursive + #return unless (userExists($this, $user)); + + # Remove the mapping id in case this is a subclass + $login =~ s/$this->{mapping_id}// if $this->{mapping_id}; + + use bytes; + + # Reverse the encoding used to generate cUIDs in login2cUID + # use bytes to ignore character encoding + $login =~ s/_([0-9a-f][0-9a-f])/chr(hex($1))/gei; + no bytes; + + return undef unless _userReallyExists( $this, $login ); + return undef unless ($cUID eq $this->login2cUID($login)); + + # Validated + return Foswiki::Sandbox::untaintUnchecked( $login ); +} + +# test if the login is in the WikiUsers topic, or in the password file +# depending on the AllowLoginNames setting +sub _userReallyExists { + my ( $this, $login ) = @_; + + if ( $Foswiki::cfg{Register}{AllowLoginName} ) { + + # need to use the WikiUsers file + _loadMapping($this); + return 1 if ( defined( $this->{L2U}->{$login} ) ); + } + + if ( $this->{passwords}->canFetchUsers() ) { + + # AllowLoginName mapping failed, maybe the user is however + # present in the Wiki managed pwd file + # can use the password file if available + my $pass = $this->{passwords}->fetchPass($login); + return unless ( defined($pass) ); + return if ( $pass eq '0' ); # login invalid... (SMELL: what + # does that really mean) + return 1; + } + else { + return 0; + } + + return 0; +} + +=begin TML + +---++ ObjectMethod addUser ($login, $wikiname, $password, $emails) -> $cUID + +throws an Error::Simple + +Add a user to the persistant mapping that maps from usernames to wikinames +and vice-versa. The default implementation uses a special topic called +"WikiUsers" in the users web. Subclasses will provide other implementations +(usually stubs if they have other ways of mapping usernames to wikinames). +Names must be acceptable to $Foswiki::cfg{NameFilter} +$login must *always* be specified. $wikiname may be undef, in which case +the user mapper should make one up. +This function must return a *canonical user id* that it uses to uniquely +identify the user. This can be the login name, or the wikiname if they +are all guaranteed unigue, or some other string consisting only of 7-bit +alphanumerics and underscores. +if you fail to create a new user (for eg your Mapper has read only access), + throw Error::Simple( + 'Failed to add user: '.$ph->error()); + +=cut + +sub addUser { + my ( $this, $login, $wikiname, $password, $emails ) = @_; + + ASSERT($login) if DEBUG; + + # SMELL: really ought to be smarter about this e.g. make a wikiword + $wikiname ||= $login; + + if ( $this->{passwords}->fetchPass($login) ) { + + # They exist; their password must match + unless ( $this->{passwords}->checkPassword( $login, $password ) ) { + throw Error::Simple( + 'New password did not match existing password for this user'); + } + + # User exists, and the password was good. + } + else { + + # add a new user + + unless ( defined($password) ) { + require Foswiki::Users; + $password = Foswiki::Users::randomPassword(); + } + + unless ( $this->{passwords}->setPassword( $login, $password ) == 1) { + + #print STDERR "\n Failed to add user: ".$this->{passwords}->error(); + throw Error::Simple( + 'Failed to add user: ' . $this->{passwords}->error() ); + } + } + + my $store = $this->{session}->{store}; + my ( $meta, $text ); + + if ( + $store->topicExists( + $Foswiki::cfg{UsersWebName}, + $Foswiki::cfg{UsersTopicName} + ) + ) + { + ( $meta, $text ) = $store->readTopic( + undef, + $Foswiki::cfg{UsersWebName}, + $Foswiki::cfg{UsersTopicName} + ); + } + else { + ( $meta, $text ) = $store->readTopic( undef, $Foswiki::cfg{SystemWebName}, + 'UsersTemplate' ); + } + + my $result = ''; + my $entry = " * $wikiname - "; + $entry .= $login . " - " if $login; + + require Foswiki::Time; + my $today = + Foswiki::Time::formatTime( time(), $Foswiki::cfg{DefaultDateFormat}, + 'gmtime' ); + + # add to the mapping caches + my $user = _cacheUser( $this, $wikiname, $login ); + ASSERT($user) if DEBUG; + + # add name alphabetically to list + + # insidelist is used to see if we are before the first record or after the last + # 0 before, 1 inside, 2 after + my $insidelist = 0; + foreach my $line ( split( /\r?\n/, $text ) ) { + + # TODO: I18N fix here once basic auth problem with 8-bit user names is + # solved + if ($entry) { + my ( $web, $name, $odate ) = ( '', '', '' ); + if ( $line =~ +/^\s+\*\s($Foswiki::regex{webNameRegex}\.)?($Foswiki::regex{wikiWordRegex})\s*(?:-\s*\w+\s*)?-\s*(.*)/ + ) + { + $web = $1 || $Foswiki::cfg{UsersWebName}; + $name = $2; + $odate = $3; + # Filter-in date format dd Mmm yyyy + $odate = '' unless $odate =~ /^\d+\s+[A-Za-z]+\s+\d+$/; + $insidelist = 1; + } + elsif ( $line =~ /^\s+\*\s([A-Z]) - / ) { + + # * A - - - - -^M + $name = $1; + $insidelist = 1; + } + elsif ( $insidelist == 1 ) { + + # After last entry we have a blank line or some comment + # We assume no blank lines inside the list of users + # We cannot look for last after Z because Z is not the last letter + # in all alphabets + $insidelist = 2; + $name = ''; + } + if ( ( $name && ( $wikiname le $name ) ) || $insidelist == 2 ) { + + # found alphabetical position or last record + if ( $wikiname eq $name ) { + + # adjusting existing user - keep original registration date + $entry .= $odate; + } + else { + $entry .= $today . "\n" . $line; + } + + # don't adjust if unchanged + return $user if ( $entry eq $line ); + $line = $entry; + $entry = ''; + } + } + + $result .= $line . "\n"; + } + if ($entry) { + + # brand new file - add to end + $result .= "$entry$today\n"; + } + try { + $store->saveTopic( + + # SMELL: why is this Admin and not the RegoAgent?? + $this->{session}->{users} + ->getCanonicalUserID( $Foswiki::cfg{AdminUserLogin} ), + $Foswiki::cfg{UsersWebName}, + $Foswiki::cfg{UsersTopicName}, + $result, $meta + ); + } + catch Error::Simple with { + + # Failed to add user; must remove them from the password system too, + # otherwise their next registration attempt will be blocked + my $e = shift; + $this->{passwords}->removeUser($login); + throw $e; + }; + +#can't call setEmails here - user may be in the process of being registered +#TODO; when registration is moved into the mapping, setEmails will happend after the createUserTOpic +#$this->setEmails( $user, $emails ); + + return $user; +} + +=begin TML + +---++ ObjectMethod removeUser( $cUID ) -> $boolean + +Delete the users entry. Removes the user from the password +manager and user mapping manager. Does *not* remove their personal +topics, which may still be linked. + +=cut + +sub removeUser { + my ( $this, $cUID ) = @_; + my $ln = $this->getLoginName($cUID); + $this->{passwords}->removeUser($ln); + + # SMELL: does not update the internal caches, + # needs someone to implement it +} + +=begin TML + +---++ ObjectMethod getWikiName ($cUID) -> $wikiname + +Map a canonical user name to a wikiname. If it fails to find a +WikiName, it will attempt to find a matching loginname, and use +an escaped version of that. +If there is no matching WikiName or LoginName, it returns undef. + +=cut + +sub getWikiName { + my ( $this, $cUID ) = @_; + ASSERT($cUID) if DEBUG; + ASSERT( $cUID =~ /^$this->{mapping_id}/ ) if DEBUG; + + my $wikiname; + + if ( $Foswiki::cfg{Register}{AllowLoginName} ) { + _loadMapping($this); + $wikiname = $this->{U2W}->{$cUID}; + } + else { + + # If the mapping isn't enabled there's no point in loading it + } + + unless ($wikiname) { + $wikiname = $this->getLoginName($cUID); + if ($wikiname) { + + # sanitise the generated WikiName + $wikiname =~ s/$Foswiki::cfg{NameFilter}//go; + } + } + + return $wikiname; +} + +=begin TML + +---++ ObjectMethod userExists($cUID) -> $boolean + +Determine if the user already exists or not. Whether a user exists +or not is determined by the password manager. + +=cut + +sub userExists { + my ( $this, $cUID ) = @_; + ASSERT($cUID) if DEBUG; + + # Do this to avoid a password manager lookup + return 1 if $cUID eq $this->{session}->{user}; + + my $loginName = $this->getLoginName($cUID); + return 0 unless defined($loginName); + + return 1 if ( $loginName eq $Foswiki::cfg{DefaultUserLogin} ); + + # Foswiki allows *groups* to log in + return 1 if ( $this->isGroup($loginName) ); + + # Look them up in the password manager (can be slow). + return 1 + if ( $this->{passwords}->canFetchUsers() + && $this->{passwords}->fetchPass($loginName) ); + + unless ( $Foswiki::cfg{Register}{AllowLoginName} + || $this->{passwords}->canFetchUsers() ) + { + + #if there is no pwd file, then its external auth + #and if AllowLoginName is also off, then the only way to know if + #the user has registered is to test for user topic? + if ( Foswiki::Func::topicExists( $Foswiki::cfg{UsersWebName}, $loginName ) ) + { + return 1; + } + } + + return 0; +} + +=begin TML + +---++ ObjectMethod eachUser () -> Foswiki::ListIterator of cUIDs + +See baseclass for documentation + +=cut + +sub eachUser { + my ($this) = @_; + + _loadMapping($this); + my @list = keys( %{ $this->{U2W} } ); + require Foswiki::ListIterator; + my $iter = new Foswiki::ListIterator( \@list ); + $iter->{filter} = sub { + + # don't claim users that are handled by the basemapping + my $cUID = $_[0] || ''; + my $login = $this->{session}->{users}->getLoginName($cUID); + my $wikiname = $this->{session}->{users}->getWikiName($cUID); + + return !( $this->{session}->{users}->{basemapping} + ->handlesUser( undef, $login, $wikiname ) ); + }; + return $iter; +} + +my %expanding; + +=begin TML + +---++ ObjectMethod eachGroupMember ($group) -> listIterator of cUIDs + +See baseclass for documentation + +=cut + +sub eachGroupMember { + my $this = shift; + my $group = shift; + + return new Foswiki::ListIterator( $this->{eachGroupMember}->{$group} ) + if ( defined( $this->{eachGroupMember}->{$group} ) ); + + my $store = $this->{session}->{store}; + my $users = $this->{session}->{users}; + + my $members = []; + if ( !$expanding{$group} + && $store->topicExists( $Foswiki::cfg{UsersWebName}, $group ) ) + { + $expanding{$group} = 1; + my $text = $store->readTopicRaw( undef, $Foswiki::cfg{UsersWebName}, + $group, undef ); + + foreach ( split( /\r?\n/, $text ) ) { + if (/$Foswiki::regex{setRegex}GROUP\s*=\s*(.+)$/) { + next unless ( $1 eq 'Set' ); + + # Note: if there are multiple GROUP assignments in the + # topic, only the last will be taken. + my $f = $2; + $members = _expandUserList( $this, $f ); + } + } + delete $expanding{$group}; + } + $this->{eachGroupMember}->{$group} = $members; + + require Foswiki::ListIterator; + return new Foswiki::ListIterator( $this->{eachGroupMember}->{$group} ); +} + +=begin TML + +---++ ObjectMethod isGroup ($user) -> boolean + +See baseclass for documentation + +=cut + +sub isGroup { + my ( $this, $user ) = @_; + + # Groups have the same username as wikiname as canonical name + return 1 if $user eq $Foswiki::cfg{SuperAdminGroup}; + + return $user =~ /Group$/; +} + +=begin TML + +---++ ObjectMethod eachGroup () -> ListIterator of groupnames + +See baseclass for documentation + +=cut + +sub eachGroup { + my ($this) = @_; + _getListOfGroups($this); + require Foswiki::ListIterator; + return new Foswiki::ListIterator( \@{ $this->{groupsList} } ); +} + +=begin TML + +---++ ObjectMethod eachMembership ($cUID) -> ListIterator of groups this user is in + +See baseclass for documentation + +=cut + +sub eachMembership { + my ( $this, $user ) = @_; + + _getListOfGroups($this); + require Foswiki::ListIterator; + my $it = new Foswiki::ListIterator( \@{ $this->{groupsList} } ); + $it->{filter} = sub { + $this->isInGroup( $user, $_[0] ); + }; + return $it; +} + +=begin TML + +---++ ObjectMethod isAdmin( $cUID ) -> $boolean + +True if the user is an admin + * is $Foswiki::cfg{SuperAdminGroup} + * is a member of the $Foswiki::cfg{SuperAdminGroup} + +=cut + +sub isAdmin { + my ( $this, $cUID ) = @_; + my $isAdmin = 0; + + # TODO: this might not apply now that we have BaseUserMapping - test + if ( $cUID eq $Foswiki::cfg{SuperAdminGroup} ) { + $isAdmin = 1; + } + else { + my $sag = $Foswiki::cfg{SuperAdminGroup}; + $isAdmin = $this->isInGroup( $cUID, $sag ); + } + + return $isAdmin; +} + +=begin TML + +---++ ObjectMethod findUserByEmail( $email ) -> \@cUIDs + * =$email= - email address to look up +Return a list of canonical user names for the users that have this email +registered with the password manager or the user mapping manager. + +The password manager is asked first for whether it maps emails. +If it doesn't, then the user mapping manager is asked instead. + +=cut + +sub findUserByEmail { + my ( $this, $email ) = @_; + ASSERT($email) if DEBUG; + my @users; + if ( $this->{passwords}->isManagingEmails() ) { + my $logins = $this->{passwords}->findUserByEmail($email); + if ( defined $logins ) { + foreach my $l (@$logins) { + $l = $this->login2cUID($l); + push( @users, $l ) if $l; + } + } + } + else { + + # if the password manager didn't want to provide the service, ask + # the user mapping manager + unless ( $this->{_MAP_OF_EMAILS} ) { + $this->{_MAP_OF_EMAILS} = {}; + my $it = $this->eachUser(); + while ( $it->hasNext() ) { + my $uo = $it->next(); + map { push( @{ $this->{_MAP_OF_EMAILS}->{$_} }, $uo ); } + $this->getEmails($uo); + } + } + push( @users, @{ $this->{_MAP_OF_EMAILS}->{$email} } ); + } + return \@users; +} + +=begin TML + +---++ ObjectMethod getEmails($name) -> @emailAddress + +If $name is a user, return their email addresses. If it is a group, +return the addresses of everyone in the group. + +The password manager and user mapping manager are both consulted for emails +for each user (where they are actually found is implementation defined). + +Duplicates are removed from the list. + +=cut + +sub getEmails { + my ( $this, $user, $seen ) = @_; + + $seen ||= {}; + + my %emails = (); + + if ( $seen->{$user} ) { + + #print STDERR "preventing infinit recursion in getEmails($user)\n"; + } + else { + $seen->{$user} = 1; + + if ( $this->isGroup($user) ) { + my $it = $this->eachGroupMember($user); + while ( $it->hasNext() ) { + foreach ( $this->getEmails( $it->next(), $seen ) ) { + $emails{$_} = 1; + } + } + } + else { + if ( $this->{passwords}->isManagingEmails() ) { + + # get emails from the password manager + foreach ( $this->{passwords} + ->getEmails( $this->getLoginName($user), $seen ) ) + { + $emails{$_} = 1; + } + } + else { + + # And any on offer from the user mapping manager + foreach ( mapper_getEmails( $this->{session}, $user ) ) { + $emails{$_} = 1; + } + } + } + } + return keys %emails; +} + +=begin TML + +---++ ObjectMethod setEmails($cUID, @emails) -> boolean + +Set the email address(es) for the given user. +The password manager is tried first, and if it doesn't want to know the +user mapping manager is tried. + +=cut + +sub setEmails { + my $this = shift; + my $user = shift; + + if ( $this->{passwords}->isManagingEmails() ) { + $this->{passwords}->setEmails( $this->getLoginName($user), @_ ); + } + else { + mapper_setEmails( $this->{session}, $user, @_ ); + } +} + +=begin TML + +---++ StaticMethod mapper_getEmails($session, $user) + +Only used if passwordManager->isManagingEmails= = =false +(The emails are stored in the user topics. + +Note: This method is PUBLIC because it is used by the tools/upgrade_emails.pl +script, which needs to kick down to the mapper to retrieve email addresses +from Wiki topics. + +=cut + +sub mapper_getEmails { + my ( $session, $user ) = @_; + + my ( $meta, $text ) = $session->{store}->readTopic( + undef, + $Foswiki::cfg{UsersWebName}, + $session->{users}->getWikiName($user) + ); + + my @addresses; + + # Try the form first + my $entry = $meta->get( 'FIELD', 'Email' ); + if ($entry) { + push( @addresses, split( /;/, $entry->{value} ) ); + } + else { + + # Now try the topic text + foreach my $l ( split( /\r?\n/, $text ) ) { + if ( $l =~ /^\s+\*\s+E-?mail:\s*(.*)$/mi ) { + # SMELL: implicit unvalidated untaint + push @addresses, split( /;/, $1 ); + } + } + } + + return @addresses; +} + +=begin TML + +---++ StaticMethod mapper_setEmails ($session, $user, @emails) + +Only used if =passwordManager->isManagingEmails= = =false=. +(emails are stored in user topics + +=cut + +sub mapper_setEmails { + my $session = shift; + my $cUID = shift; + + my $mails = join( ';', @_ ); + + my $user = $session->{users}->getWikiName($cUID); + + my ( $meta, $text ) = + $session->{store}->readTopic( undef, $Foswiki::cfg{UsersWebName}, $user ); + + if ( $meta->get('FORM') ) { + + # use the form if there is one + $meta->putKeyed( + 'FIELD', + { + name => 'Email', + value => $mails, + title => 'Email', + attributes => 'h' + } + ); + } + else { + + # otherwise use the topic text + unless ( $text =~ s/^(\s+\*\s+E-?mail:\s*).*$/$1$mails/mi ) { + $text .= "\n * Email: $mails\n"; + } + } + + $session->{store} + ->saveTopic( $cUID, $Foswiki::cfg{UsersWebName}, $user, $text, $meta ); +} + +=begin TML + +---++ ObjectMethod findUserByWikiName ($wikiname) -> list of cUIDs associated with that wikiname + +See baseclass for documentation + +The $skipExistanceCheck parameter +is private to this module, and blocks the standard existence check +to avoid reading .htpasswd when checking group memberships). + +=cut + +sub findUserByWikiName { + my ( $this, $wn, $skipExistanceCheck ) = @_; + my @users = (); + + if ( $this->isGroup($wn) ) { + push( @users, $wn ); + } + elsif ( $Foswiki::cfg{Register}{AllowLoginName} ) { + + # Add additional mappings defined in WikiUsers + _loadMapping($this); + if ( $this->{W2U}->{$wn} ) { + + # Wikiname to UID mapping is defined + my $user = $this->{W2U}->{$wn}; + push( @users, $user ) if $user; + } + else { + + # Bloody compatibility! + # The wikiname is always a registered user for the purposes of this + # mapping. We have to do this because Foswiki defines access controls + # in terms of mapped users, and if a wikiname is *missing* from the + # mapping there is "no such user". + my $user = $this->login2cUID($wn); + push( @users, $user ) if $user; + } + } + else { + + # The wikiname is also the login name, so we can just convert + # it directly to a cUID + my $cUID = $this->login2cUID($wn); + if ( $skipExistanceCheck || ( $cUID && $this->userExists($cUID) ) ) { + push( @users, $cUID ); + } + } + return \@users; +} + +=begin TML + +---++ ObjectMethod checkPassword( $login, $password ) -> $boolean + +Finds if the password is valid for the given user. + +Returns 1 on success, undef on failure. + +=cut + +sub checkPassword { + my ( $this, $login, $pw ) = @_; + return $this->{passwords}->checkPassword( $login, $pw ); +} + +=begin TML + +---++ ObjectMethod setPassword( $cUID, $newPassU, $oldPassU ) -> $boolean + +BEWARE: $user should be a cUID, but is a login when the resetPassword +functionality is used. +The UserMapper needs to convert either one to a valid login for use by +the Password manager + +TODO: needs fixing + +If the $oldPassU matches matches the user's password, then it will +replace it with $newPassU. + +If $oldPassU is not correct and not 1, will return 0. + +If $oldPassU is 1, will force the change irrespective of +the existing password, adding the user if necessary. + +Otherwise returns 1 on success, undef on failure. + +=cut + +sub setPassword { + my ( $this, $user, $newPassU, $oldPassU ) = @_; + ASSERT( $user ) if DEBUG; + my $login = $this->getLoginName($user) || $user; + return $this->{passwords} + ->setPassword( $login, $newPassU, $oldPassU ); +} + +=begin TML + +---++ ObjectMethod passwordError( ) -> $string + +returns a string indicating the error that happened in the password handlers +TODO: these delayed error's should be replaced with Exceptions. + +returns undef if no error + +=cut + +sub passwordError { + my ($this) = @_; + return $this->{passwords}->error(); +} + +# TODO: and probably flawed in light of multiple cUIDs mapping to one wikiname +sub _cacheUser { + my ( $this, $wikiname, $login ) = @_; + ASSERT($wikiname) if DEBUG; + + $login ||= $wikiname; + + #discard users that are the BaseUserMapper's responsibility + return if ( $this->{session}->{users}->{basemapping} + ->handlesUser( undef, $login, $wikiname ) ); + + my $cUID = $this->login2cUID( $login, 1 ); + return unless ($cUID); + ASSERT($cUID) if DEBUG; + + #$this->{U2L}->{$cUID} = $login; + $this->{U2W}->{$cUID} = $wikiname; + $this->{L2U}->{$login} = $cUID; + $this->{W2U}->{$wikiname} = $cUID; + + return $cUID; +} + +# callback for search function to collate results +sub _collateGroups { + my $ref = shift; + my $group = shift; + return unless $group; + push( @{ $ref->{list} }, $group ); +} + +# get a list of groups defined in this Wiki +sub _getListOfGroups { + my $this = shift; + ASSERT( ref($this) eq 'Foswiki::Users::TopicUserMapping' ) if DEBUG; + + unless ( $this->{groupsList} ) { + my $users = $this->{session}->{users}; + $this->{groupsList} = []; + + $this->{session}->search->searchWeb( + _callback => \&_collateGroups, + _cbdata => { + list => $this->{groupsList}, + users => $users + }, + inline => 1, + search => "Set GROUP =", + web => $Foswiki::cfg{UsersWebName}, + topic => "*Group", + type => 'regex', + nosummary => 'on', + nosearch => 'on', + noheader => 'on', + nototal => 'on', + noempty => 'on', + format => '$topic', + separator => '', + ); + } + return $this->{groupsList}; +} + +# Build hash to translate between username (e.g. jsmith) +# and WikiName (e.g. Main.JaneSmith). +# PRIVATE subclasses should *not* implement this. +sub _loadMapping { + my $this = shift; + return if $this->{CACHED}; + $this->{CACHED} = 1; + + #TODO: should only really do this mapping IF the user is in the password file. + # except if we can't 'fetchUsers' like in the Passord='none' case - + # in which case the only time we + # know a login is real, is when they are logged in :( + if ( ( $Foswiki::cfg{Register}{AllowLoginName} ) + || ( !$this->{passwords}->canFetchUsers() ) ) + { + my $store = $this->{session}->{store}; + if ( + $store->topicExists( + $Foswiki::cfg{UsersWebName}, + $Foswiki::cfg{UsersTopicName} + ) + ) + { + my $text = $store->readTopicRaw( + undef, + $Foswiki::cfg{UsersWebName}, + $Foswiki::cfg{UsersTopicName}, undef + ); + + # Get the WikiNames and userids, and build hashes in both directions + # This matches: + # * WikiGuest - guest - 10 Mar 2005 + # * WikiGuest - 10 Mar 2005 + $text =~ +s/^\s*\* (?:$Foswiki::regex{webNameRegex}\.)?($Foswiki::regex{wikiWordRegex})\s*(?:-\s*(\S+)\s*)?-.*$/(_cacheUser( $this, $1, $2)||'')/gome; + } + } + else { + + #loginnames _are_ WikiNames so ask the Password handler for list of users + my $iter = $this->{passwords}->fetchUsers(); + while ( $iter->hasNext() ) { + my $login = $iter->next(); + _cacheUser( $this, $login, $login ); + } + } +} + +# Get a list of *canonical user ids* from a text string containing a +# list of user *wiki* names, *login* names, and *group ids*. +sub _expandUserList { + my ( $this, $names ) = @_; + + $names ||= ''; + + # comma delimited list of users or groups + # i.e.: "%MAINWEB%.UserA, UserB, Main.UserC # something else" + $names =~ s/(<[^>]*>)//go; # Remove HTML tags + + my @l; + foreach my $ident ( split( /[\,\s]+/, $names ) ) { + + # Dump the web specifier if userweb + $ident =~ s/^($Foswiki::cfg{UsersWebName}|%USERSWEB%|%MAINWEB%)\.//; + next unless $ident; + if ( $this->isGroup($ident) ) { + my $it = $this->eachGroupMember($ident); + while ( $it->hasNext() ) { + push( @l, $it->next() ); + } + } + else { + + # Might be a wiki name (wiki names may map to several cUIDs) + my %namelist = + map { $_ => 1 } + @{ $this->{session}->{users}->findUserByWikiName($ident) }; + + # May be a login name (login names map to a single cUID) + my $cUID = $this->{session}->{users}->getCanonicalUserID($ident); + $namelist{$cUID} = 1 if $cUID; + push( @l, keys %namelist ); + } + } + return \@l; +} + +1; diff --git a/TopicUserMappingContrib/test/unit/TopicUserMappingContrib/TopicUserMappingContribSuite.pm b/TopicUserMappingContrib/test/unit/TopicUserMappingContrib/TopicUserMappingContribSuite.pm new file mode 100644 index 0000000000..82aa8733f6 --- /dev/null +++ b/TopicUserMappingContrib/test/unit/TopicUserMappingContrib/TopicUserMappingContribSuite.pm @@ -0,0 +1,8 @@ +package TopicUserMappingContribSuite; +use base 'Unit::TestSuite'; + +sub include_tests { + return qw(TopicUserMappingContribTests TopicUserMappingTests); +} + +1; diff --git a/TopicUserMappingContrib/test/unit/TopicUserMappingContrib/TopicUserMappingContribTests.pm b/TopicUserMappingContrib/test/unit/TopicUserMappingContrib/TopicUserMappingContribTests.pm new file mode 100755 index 0000000000..0f6af22aec --- /dev/null +++ b/TopicUserMappingContrib/test/unit/TopicUserMappingContrib/TopicUserMappingContribTests.pm @@ -0,0 +1,339 @@ +use strict; + +# +# Tests the TopicUserMappingContrib, including dealing with legacy login +# names and wiki names stored in topics, in TOPICINFO and FILEATTACHMENT +# meta-data. +# +# Only works with the RCS store. +# +package TopicUserMappingContribTests; + +use base qw( FoswikiFnTestCase ); + +use Unit::Request; +use Unit::Response; +use Foswiki; +use Error qw( :try ); + +my $TopicTemplate = <<'THIS'; +some text that is there. + +%META:FILEATTACHMENT{name="home.org.au.png" attachment="home.org.au.png" attr="" comment="" date="1180648704" path="home.org.au.png" size="4170" stream="home.org.au.png" user="UUUUUUUUUU" version="1"}% +THIS + +sub new { + my $self = shift()->SUPER::new( 'TopicUserMappingContribTests', @_ ); + return $self; +} + +sub fixture_groups { + return ( [ 'NormalTopicUserMapping', 'NamedTopicUserMapping', ] ); +} + +sub NormalTopicUserMapping { + my $this = shift; + $Foswiki::Users::TopicUserMapping::TWIKI_USER_MAPPING_ID = ''; + $this->set_up_for_verify(); +} + +sub NamedTopicUserMapping { + my $this = shift; + + # Set a mapping ID for purposes of testing named mappings + $Foswiki::Users::TopicUserMapping::TWIKI_USER_MAPPING_ID = 'TestMapping_'; + $this->set_up_for_verify(); +} + +# Override default set_up in base class; will call it after the mapping +# id has been set +sub set_up { +} + +# Delay the calling of set_up till after the cfg's are set by above closure +sub set_up_for_verify { + my $this = shift; + + $this->SUPER::set_up(); + + $this->assert( + $Foswiki::cfg{StoreImpl} =~ /^Rcs/, + "Test does not run with non-RCS store" + ); + + #default settings + $Foswiki::cfg{LoginManager} = 'Foswiki::LoginManager::TemplateLogin'; + $Foswiki::cfg{UserMappingManager} = 'Foswiki::Users::TopicUserMapping'; + $Foswiki::cfg{UseClientSessions} = 1; + $Foswiki::cfg{PasswordManager} = "Foswiki::Users::HtPasswdUser"; + $Foswiki::cfg{Register}{EnableNewUserRegistration} = 1; + $Foswiki::cfg{Register}{AllowLoginName} = 0; + $Foswiki::cfg{DisplayTimeValues} = 'gmtime'; +} + +sub setup_new_session() { + my $this = shift; + + my ( $query, $text ); + $query = new Unit::Request( {} ); + $query->path_info("/Main/WebHome"); + $ENV{SCRIPT_NAME} = "view"; + + # close this Foswiki session - its using the wrong mapper and login + $this->{twiki}->finish(); + $this->{twiki} = new Foswiki( undef, $query ); +} + +sub set_up_user { + my $this = shift; + + my $userLogin; + my $userWikiName; + my $user_id; + + if ( $this->{twiki}->{users}->supportsRegistration() ) { + $userWikiName = 'JoeDoe'; + $userLogin = $userWikiName; + $userLogin = 'joe' if ( $Foswiki::cfg{Register}{AllowLoginName} ); + $user_id = + $this->{twiki}->{users} + ->addUser( $userLogin, $userWikiName, 'secrect_password', + 'email@home.org.au' ); + $this->annotate( +"create $userLogin user - cUID = $user_id , login $userLogin , wikiname: $userWikiName\n" + ); + } + else { + $userLogin = $Foswiki::cfg{AdminUserLogin}; + $user_id = $this->{twiki}->{users}->getCanonicalUserID($userLogin); + $userWikiName = $this->{twiki}->{users}->getWikiName($user_id); + $this->annotate("no rego support (using admin)\n"); + } + $this->{userLogin} = $userLogin; + $this->{userWikiName} = $userWikiName; + $this->{user_id} = $user_id; +} + +#TODO: add tests for when you're not using TopicUserMapping at all... +#New 4.2 cUID based topics +sub verify_WikiNameTopicUserMapping { + my $this = shift; + $this->setup_new_session(); + $this->set_up_user(); + $this->std_tests( $this->{user_id}, + $this->{twiki}->{users}->webDotWikiName( $this->{user_id} ) ); +} + +sub verify_LoginNameTopicUserMapping { + my $this = shift; + $Foswiki::cfg{Register}{AllowLoginName} = 1; + $this->setup_new_session(); + $this->set_up_user(); + $this->std_tests( $this->{user_id}, + $this->{twiki}->{users}->webDotWikiName( $this->{user_id} ) ); +} + +#legacy topic forms +sub verify_valid_login_no_Mapper_in_cUID { + my $this = shift; + $Foswiki::cfg{Register}{AllowLoginName} = 1; + $this->setup_new_session(); + $this->set_up_user(); + $this->std_tests( $this->{userLogin}, + $this->{twiki}->{users}->webDotWikiName( $this->{user_id} ) ); +} + +sub verify_valid_wikiname_no_Mapper_in_cUID { + my $this = shift; + $Foswiki::cfg{Register}{AllowLoginName} = 1; + $this->setup_new_session(); + $this->set_up_user(); + $this->std_tests( $this->{userWikiName}, + $this->{twiki}->{users}->webDotWikiName( $this->{user_id} ) ); +} + +sub verify_web_and_wikiname_no_Mapper_in_cUID { + my $this = shift; + $Foswiki::cfg{Register}{AllowLoginName} = 1; + $this->setup_new_session(); + $this->set_up_user(); + $this->std_tests( + $this->{twiki}->{users}->webDotWikiName( $this->{user_id} ), + $this->{twiki}->{users}->webDotWikiName( $this->{user_id} ) + ); +} + +sub verify_valid_login_no_Mapper_in_cUID_NOAllowLoginName { + my $this = shift; + + #$Foswiki::cfg{Register}{AllowLoginName} = 1; + $this->setup_new_session(); + $this->set_up_user(); + $this->std_tests( $this->{userLogin}, + $this->{twiki}->{users}->webDotWikiName( $this->{user_id} ) ); +} + +sub verify_valid_wikiname_no_Mapper_in_cUID_NOAllowLoginName { + my $this = shift; + + #$Foswiki::cfg{Register}{AllowLoginName} = 1; + $this->setup_new_session(); + $this->set_up_user(); + $this->std_tests( $this->{userWikiName}, + $this->{twiki}->{users}->webDotWikiName( $this->{user_id} ) ); +} + +sub verify_web_and_wikiname_no_Mapper_in_cUID_NOAllowLoginName { + my $this = shift; + + #$Foswiki::cfg{Register}{AllowLoginName} = 1; + $this->setup_new_session(); + $this->set_up_user(); + $this->std_tests( + $this->{twiki}->{users}->webDotWikiName( $this->{user_id} ), + $this->{twiki}->{users}->webDotWikiName( $this->{user_id} ) + ); +} + +#error and fallback tests +sub TODOtest_non_existantIser { + my $this = shift; + + #$Foswiki::cfg{Register}{AllowLoginName} = 1; + $this->setup_new_session(); + $this->set_up_user(); + $this->std_tests( 'nonexistantUser', $this->{users_web} . '.UnknownUser' ); +} + +sub std_tests { + my ( $this, $serializedName, $displayedName ) = @_; + $this->annotate( + "topic contains: $serializedName, rendered : $displayedName\n"); + + my $CuidWithMappers = $TopicTemplate; + $CuidWithMappers =~ s/UUUUUUUUUU/$serializedName/e; + + $this->assert_not_null( $this->{twiki}->{user} ); + $this->{twiki}->{store}->saveTopic( + $this->{twiki}->{user}, $this->{test_web}, + 'CuidWithMappers', $CuidWithMappers + ); + + #test that all 4 raw internal values are ok cUIDs + my ( $date, $user, $rev, $comment ) = + $this->{twiki}->{store} + ->getRevisionInfo( $this->{test_web}, 'CuidWithMappers' ); + $this->assert_not_null($user); + + my ( $meta, $text ) = + $this->{twiki}->{store}->readTopic( $this->{twiki}->{user}, + $this->{test_web}, 'CuidWithMappers' ); + + my $topicinfo = $meta->get('TOPICINFO'); + $this->assert_not_null( $topicinfo->{author} ); + $this->assert_str_equals( 'BaseUserMapping_666', $topicinfo->{author} ) + ; #render the topic, make sure we're seeing NO cUIDs, and WikiNames for all known users + #parse meta output + $this->assert( $meta->count("FILEATTACHMENT") == 1, "Should be one item" ); + + my $file1 = $meta->get('FILEATTACHMENT'); + $this->assert_not_null( $file1->{'user'} ); + $this->assert_str_equals( 'home.org.au.png', $file1->{'name'} ); + + my @attachments = $meta->find('FILEATTACHMENT'); + foreach my $attachment (@attachments) { + $this->annotate( "FILEATTACHMENT user = " . $attachment->{'user'} ); + $this->assert_not_null( $attachment->{'user'} ); + } + + #test func outputs + + #peek at old rev's to see what rcs tells us + #render diff & history to make sure those are all wikiname + + #render attahcment tables, and rev history of attachment tables, + #all must be wikiname + my $renderedMeta = + $this->{twiki} + ->attach->renderMetaData( $this->{test_web}, 'CuidWithMappers', $meta, + { template => 'attachtables.tmpl' } ); + $this->assert_not_null($renderedMeta); + + #TODO: redo this with custom tmpl and check each username + my $output = <<'THIS'; +
+| *I* | *%MAKETEXT{"Attachment"}%* | *%MAKETEXT{"Action"}%* | *%MAKETEXT{"Size"}%* | *%MAKETEXT{"Date"}%* | *%MAKETEXT{"Who"}%* | *%MAKETEXT{"Comment"}%* | +| pngpng | home.org.au.png | %MAKETEXT{"manage"}% | 4.1 K|31 May 2007 - 21:58 |TemporaryTopicUserMappingContribTestsUsersWeb.JoeDoe |  | +
+THIS + $output =~ s/UUUUUUUUUU/$displayedName/e; + $output =~ s/%PUBURLPATH%/$Foswiki::cfg{PubUrlPath}/e; + $output =~ s/%EXPANDEDPUBURL%/$Foswiki::cfg{PubUrlPath}/e; + $this->assert_str_equals( $output, $renderedMeta . "\n" ); + + #see if leases and locks have similar issues +} + +########################################### +sub verify_BaseMapping_handleUser { + my $this = shift; + my $basemapping = $this->{twiki}->{users}->{basemapping}; + + #ObjectMethod handlesUser ( $cUID, $login, $wikiname) + $this->assert( !$basemapping->handlesUser() ); + + $this->assert( + $basemapping->handlesUser( undef, $Foswiki::cfg{AdminUserLogin} ) ); + $this->assert( + $basemapping->handlesUser( undef, $Foswiki::cfg{DefaultUserLogin} ) ); + $this->assert( $basemapping->handlesUser( undef, 'unknown' ) ); + $this->assert( $basemapping->handlesUser( undef, 'ProjectContributor' ) ); + $this->assert( $basemapping->handlesUser( undef, $Foswiki::cfg{Register}{RegistrationAgentWikiName} ) ); + + $this->assert( + $basemapping->handlesUser( + undef, undef, $Foswiki::cfg{AdminUserWikiName} + ) + ); + $this->assert( + $basemapping->handlesUser( + undef, undef, $Foswiki::cfg{DefaultUserWikiName} + ) + ); + $this->assert( $basemapping->handlesUser( undef, undef, 'UnknownUser' ) ); + $this->assert( + $basemapping->handlesUser( undef, undef, 'ProjectContributor' ) ); + $this->assert( + $basemapping->handlesUser( undef, undef, $Foswiki::cfg{Register}{RegistrationAgentWikiName} ) ); + + $this->assert( + $basemapping->handlesUser( + undef, $Foswiki::cfg{AdminUserLogin}, + $Foswiki::cfg{AdminUserWikiName} + ) + ); + $this->assert( + $basemapping->handlesUser( + undef, $Foswiki::cfg{DefaultUserLogin}, + $Foswiki::cfg{DefaultUserWikiName} + ) + ); + $this->assert( + $basemapping->handlesUser( undef, 'unknown', 'UnknownUser' ) ); + $this->assert( + $basemapping->handlesUser( + undef, 'ProjectContributor', 'ProjectContributor' + ) + ); + $this->assert( + $basemapping->handlesUser( + undef, $Foswiki::cfg{Register}{RegistrationAgentWikiName}, $Foswiki::cfg{Register}{RegistrationAgentWikiName} + ) + ); + + #TODO: work out what we'd like to have happen with bad combinations + + #TODO: users not in any mapping, and ones in the main mapping +} + +1; diff --git a/TopicUserMappingContrib/test/unit/TopicUserMappingContrib/TopicUserMappingTests.pm b/TopicUserMappingContrib/test/unit/TopicUserMappingContrib/TopicUserMappingTests.pm new file mode 100644 index 0000000000..973a2eb988 --- /dev/null +++ b/TopicUserMappingContrib/test/unit/TopicUserMappingContrib/TopicUserMappingTests.pm @@ -0,0 +1,314 @@ +use strict; + +package TopicUserMappingTests; + +# Some basic tests for Foswiki::Users::TopicUserMapping +# +# The tests are performed using the APIs published by the facade class, +# Foswiki:Users, not the actual Foswiki::Users::TopicUserMapping + +use base qw(FoswikiTestCase); + +use Foswiki; +use Foswiki::Users; +use Foswiki::Users::TopicUserMapping; +use Error qw( :try ); + +my $twiki; +my $saveTopic; +my $ttpath; + +my $testSysWeb = 'TemporaryTopicUserMappingTestsSystemWeb'; +my $testNormalWeb = "TemporaryTopicUserMappingTestsNormalWeb"; +my $testUsersWeb = "TemporaryTopicUserMappingTestsUsersWeb"; +my $testUser; + +sub fixture_groups { + return ( [ 'useHtpasswdMgr', 'noPasswdMgr'], + [ 'NormalTopicUserMapping', 'NamedTopicUserMapping', ]); +} + +sub NormalTopicUserMapping { + my $this = shift; + $Foswiki::Users::TopicUserMapping::TWIKI_USER_MAPPING_ID = ''; + $this->set_up_for_verify(); +} + +sub NamedTopicUserMapping { + my $this = shift; + + # Set a mapping ID for purposes of testing named mappings + $Foswiki::Users::TopicUserMapping::TWIKI_USER_MAPPING_ID = 'TestMapping_'; + $this->set_up_for_verify(); +} + +sub useHtpasswdMgr { + my $this = shift; + + $Foswiki::cfg{PasswordManager} = "Foswiki::Users::HtPasswdUser"; +} +sub noPasswdMgr { + my $this = shift; + + $Foswiki::cfg{PasswordManager} = "none"; +} +# Override default set_up in base class; will call it after the mapping +# id has been set +sub set_up { +} + +# Delay the calling of set_up till after the cfg's are set by above closure +sub set_up_for_verify { + my $this = shift; + + $this->SUPER::set_up(); + + my $original = $Foswiki::cfg{SystemWebName}; + $Foswiki::cfg{Htpasswd}{FileName} = "$Foswiki::cfg{TempfileDir}/junkhtpasswd"; + $Foswiki::cfg{UsersWebName} = $testUsersWeb; + $Foswiki::cfg{SystemWebName} = $testSysWeb; + $Foswiki::cfg{LocalSitePreferences} = "$testUsersWeb.SitePreferences"; + $Foswiki::cfg{UserMappingManager} = 'Foswiki::Users::TopicUserMapping'; + $Foswiki::cfg{Register}{AllowLoginName} = 1; + $Foswiki::cfg{Register}{EnableNewUserRegistration} = 1; + + try { + $twiki = new Foswiki( $Foswiki::cfg{AdminUserLogin} ); + $twiki->{store}->createWeb( $twiki->{user}, $testUsersWeb ); + + # the group is recursive to force a recursion block + $twiki->{store}->saveTopic( + $twiki->{user}, $testUsersWeb, + $Foswiki::cfg{SuperAdminGroup}, + " * Set GROUP = $Foswiki::cfg{SuperAdminGroup}\n" + ); + + $twiki->{store}->createWeb( $twiki->{user}, $testSysWeb, $original ); + $twiki->{store} + ->createWeb( $twiki->{user}, $testNormalWeb, '_default' ); + + $twiki->{store}->copyTopic( + $twiki->{user}, $original, + $Foswiki::cfg{SitePrefsTopicName}, $testSysWeb, + $Foswiki::cfg{SitePrefsTopicName} + ); + + $testUser = $this->createFakeUser($twiki); + } + catch Foswiki::AccessControlException with { + my $e = shift; + $this->assert( 0, $e->stringify() ); + } + catch Error::Simple with { + $this->assert( 0, shift->stringify() || '' ); + }; +} + +sub tear_down { + my $this = shift; + + $this->removeWebFixture( $twiki, $testUsersWeb ); + $this->removeWebFixture( $twiki, $testSysWeb ); + $this->removeWebFixture( $twiki, $testNormalWeb ); + unlink $Foswiki::cfg{Htpasswd}{FileName}; + $twiki->finish(); + $this->SUPER::tear_down(); +} + +sub new { + my $self = shift()->SUPER::new(@_); + return $self; +} + +my $initial = <<'THIS'; + * A - - - - - + * AttilaTheHun - 10 Jan 1601 + * B - - - - - + * BungditDin - 10 Jan 2004 + * C - - - - - + * D - - - - - + * E - - - - - + * F - - - - - + * G - - - - - + * GungaDin - 10 Jan 2004 + * H - - - - - + * I - - - - - + * J - - - - - + * K - - - - - + * L - - - - - + * M - - - - - + * N - - - - - + * O - - - - - + * P - - - - - + * Q - - - - - + * R - - - - - + * S - - - - - + * SadOldMan - sad - 10 Jan 2004 + * SorryOldMan - 10 Jan 2004 + * StupidOldMan - 10 Jan 2004 + * T - - - - - + * U - - - - - + * V - - - - - + * W - - - - - + * X - - - - - + * Y - - - - - + * Z - - - - - +THIS + +sub createFakeUser { + my ( $this, $twiki, $text, $name ) = @_; + $this->assert( $twiki->{store}->webExists( $Foswiki::cfg{UsersWebName} ) ); + $name ||= ''; + my $base = "TemporaryTestUser" . $name; + my $i = 0; + while ( + $twiki->{store}->topicExists( $Foswiki::cfg{UsersWebName}, $base . $i ) ) + { + $i++; + } + $text ||= ''; + my $meta = new Foswiki::Meta( $twiki, $Foswiki::cfg{UsersWebName}, $base . $i ); + $meta->put( + "TOPICPARENT", + { + name => $Foswiki::cfg{UsersWebName} . '.' . $Foswiki::cfg{HomeTopicName} + } + ); + $twiki->{store}->saveTopic( $twiki->{user}, $Foswiki::cfg{UsersWebName}, + $base . $i, $text, $meta ); + push( @{ $this->{fake_users} }, $base . $i ); + return $base . $i; +} + +sub verify_AddUsers { + my $this = shift; + my $ttpath = +"$Foswiki::cfg{DataDir}/$Foswiki::cfg{UsersWebName}/$Foswiki::cfg{UsersTopicName}.txt"; + my $me = $Foswiki::cfg{Register}{RegistrationAgentWikiName}; + + open( F, ">$ttpath" ) || $this->assert( 0, "open $ttpath failed" ); + print F $initial; + close(F); + chmod( 0777, $ttpath ); + $twiki->{users}->{mapping}->addUser( "guser", "GeorgeUser", $me ); + open( F, "<$ttpath" ); + local $/ = undef; + my $text = ; + close(F); + $this->assert_matches( + qr/\n\s+\* GeorgeUser - guser - \d\d \w\w\w \d\d\d\d\n/s, $text ); + $twiki->{users}->{mapping}->addUser( "auser", "AaronUser", $me ); + open( F, "<$ttpath" ); + local $/ = undef; + $text = ; + close(F); + $this->assert_matches( qr/AaronUser.*GeorgeUser/s, $text ); + $twiki->{users}->{mapping}->addUser( "zuser", "ZebediahUser", $me ); + open( F, "<$ttpath" ); + local $/ = undef; + $text = ; + close(F); + $this->assert_matches( qr/Aaron.*George.*Zebediah/s, $text ); +} + +sub verify_Load { + my $this = shift; + + my $me = $Foswiki::cfg{Register}{RegistrationAgentWikiName}; + $ttpath = +"$Foswiki::cfg{DataDir}/$Foswiki::cfg{UsersWebName}/$Foswiki::cfg{UsersTopicName}.txt"; + + open( F, ">$ttpath" ) || $this->assert( 0, "open $ttpath failed" ); + print F $initial; + close(F); + + my $zuser_id = + $twiki->{users}->{mapping}->addUser( "zuser", "ZebediahUser", $me ); + my $auser_id = + $twiki->{users}->{mapping}->addUser( "auser", "AaronUser", $me ); + my $guser_id = + $twiki->{users}->{mapping}->addUser( "guser", "GeorgeUser", $me ); + + # deliberate repeat + $twiki->{users}->{mapping}->addUser( "zuser", "ZebediahUser", $me ); + + # find a nonexistent user to force a cache read + $twiki->finish(); + $twiki = new Foswiki(); + my $n = $twiki->{users}->{mapping}->login2cUID("auser"); + $this->assert_str_equals( $n, $auser_id ); + $this->assert_str_equals( "AaronUser", $twiki->{users}->getWikiName($n) ); + $this->assert_str_equals( "auser", $twiki->{users}->getLoginName($n) ); + + my $i = $twiki->{users}->eachUser(); + my @l = (); + while ( $i->hasNext() ) { + push( @l, $i->next() ); + } + my $k = join( ",", sort map { $twiki->{users}->getWikiName($_) } @l ); + $this->assert( $k =~ s/^AaronUser,//, $k ); + $this->assert( $k =~ s/^AdminUser,//, $k ); + $this->assert( $k =~ s/^AttilaTheHun,//, $k ); + $this->assert( $k =~ s/^BungditDin,//, $k ); + $this->assert( $k =~ s/^GeorgeUser,//, $k ); + $this->assert( $k =~ s/^GungaDin,//, $k ); + $this->assert( $k =~ s/^ProjectContributor,//, $k ); + $this->assert( $k =~ s/^RegistrationAgent,//, $k ); + $this->assert( $k =~ s/^SadOldMan,//, $k ); + $this->assert( $k =~ s/^SorryOldMan,//, $k ); + $this->assert( $k =~ s/^StupidOldMan,//, $k ); + $this->assert( $k =~ s/^UnknownUser,//, $k ); + $this->assert( $k =~ s/^WikiGuest,//, $k ); + $this->assert( $k =~ s/^ZebediahUser//, $k ); + $this->assert_str_equals( "", $k ); +} + +sub groupFix { + my $this = shift; + my $me = $Foswiki::cfg{Register}{RegistrationAgentWikiName}; + $twiki->{users}->{mapping}->addUser( "auser", "AaronUser", $me ); + $twiki->{users}->{mapping}->addUser( "guser", "GeorgeUser", $me ); + $twiki->{users}->{mapping}->addUser( "zuser", "ZebediahUser", $me ); + $twiki->{users}->{mapping}->addUser( "auser", "AaronUser", $me ); + $twiki->{users}->{mapping}->addUser( "guser", "GeorgeUser", $me ); + $twiki->{users}->{mapping}->addUser( "zuser", "ZebediahUser", $me ); + $twiki->{users}->{mapping}->addUser( "scum", "ScumUser", $me ); + $twiki->{store}->saveTopic( $twiki->{user}, $testUsersWeb, 'AmishGroup', + " * Set GROUP = AaronUser,%MAINWEB%.GeorgeUser, scum\n" ); + $twiki->{store}->saveTopic( $twiki->{user}, $testUsersWeb, 'BaptistGroup', + " * Set GROUP = GeorgeUser,$testUsersWeb.ZebediahUser\n" ); +} + +sub verify_getListOfGroups { + my $this = shift; + $this->groupFix(); + my $i = $twiki->{users}->eachGroup(); + my @l = (); + while ( $i->hasNext() ) { push( @l, $i->next() ) } + my $k = join( ',', sort @l ); + $this->assert_str_equals( + "AdminGroup,AmishGroup,BaptistGroup,BaseGroup", $k ); +} + +sub verify_groupMembers { + my $this = shift; + $this->groupFix(); + my $g = "AmishGroup"; + $this->assert( $twiki->{users}->isGroup($g) ); + my $i = $twiki->{users}->eachGroupMember($g); + my @l = (); + while ( $i->hasNext() ) { push( @l, $i->next() ) } + my $k = join( ',', map { $twiki->{users}->getLoginName($_) } sort @l ); + $this->assert_str_equals( "auser,guser,scum", $k ); + $g = "BaptistGroup"; + $this->assert( $twiki->{users}->isGroup($g) ); + + $i = $twiki->{users}->eachGroupMember($g); + @l = (); + while ( $i->hasNext() ) { push( @l, $i->next() ) } + $k = join( ',', map { $twiki->{users}->getLoginName($_) } sort @l ); + $this->assert_str_equals( "guser,zuser", $k ); + +} + +1; diff --git a/TopicUserMappingContrib/tools/upgrade_emails.pl b/TopicUserMappingContrib/tools/upgrade_emails.pl new file mode 100755 index 0000000000..87bb88ff1b --- /dev/null +++ b/TopicUserMappingContrib/tools/upgrade_emails.pl @@ -0,0 +1,81 @@ +#!perl +# +# This script will iterate over the list of users in the Wiki users +# topic, recovering the email for each user (which will get the email +# from the user topic if it isn't found in the secret DB) and then +# setting the email in the secret DB. This will *not* modify the +# user topics. +# +# A default admin e-mail address will be used for users without an +# e-mail address currently in their user topic. +# + +use strict; + +BEGIN { + require 'setlib.cfg'; +}; + +use Foswiki; +use Foswiki::Users::TopicUserMapping; # required to get email addresses + +my $foswiki = new Foswiki(); + +my $admin_email = $Foswiki::cfg{WebMasterEmail} || 'webmaster@example.com'; +$/ = "\n"; + +print <; + chomp $n; + last if( !$n ); + $admin_email = $n; +}; + +my ($meta, $text) = + $foswiki->{store}->readTopic( + undef, $Foswiki::cfg{UsersWebName}, $Foswiki::cfg{UsersTopicName} ); + +my $users = $foswiki->{users}; + +foreach my $line ( split( /\r?\n/, $text )) { + if( $line =~ /^\s*\* ($Foswiki::regex{webNameRegex}\.)?(\w+)\s*(?:-\s*(\S+)\s*)?-\s*\d+ \w+ \d+\s*$/o ) { + my $web = $1 || $Foswiki::cfg{UsersWebName}; + my $wn = $2 || ''; # WikiName + my $un = $3 || $wn; # userid + my $id = ($un eq $wn) ? $wn : "$un:$wn"; + + my $cUID = $users->getCanonicalUserID($un); + if ($cUID) { + # Use an eval in case there is a problem setting the email + eval { + # Get emails *from the password manager* + my @em = $users->getEmails($cUID); + if (scalar(@em)) { + print "Already have an address for $id\n"; + } else { + # Get emails *from the Foswiki user mapping manager* + @em = Foswiki::Users::TopicUserMapping::mapper_getEmails( + $foswiki, $cUID); + if( scalar( @em )) { + print "Secreting $id: ",join(';',@em),"\n"; + $users->setEmails( $cUID, @em ); + } else { + print "No email address found for $id, using $admin_email\n"; + $users->setEmails( $cUID, $admin_email ); + } + } + }; + print "Warning: $@" if $@; + } else { + print "User $id not found in users database\n" + } + } +} +