← Index
NYTProf Performance Profile   « block view • line view • sub view »
For /usr/local/src/github.com/foswiki/core/bin/view
  Run on Sun Dec 4 17:17:59 2011
Reported on Sun Dec 4 17:26:45 2011

Filename/usr/local/src/github.com/foswiki/core/lib/Foswiki/Users/TopicUserMapping.pm
StatementsExecuted 45320 statements in 222ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
1222653143ms3.53sFoswiki::Users::TopicUserMapping::::isGroupFoswiki::Users::TopicUserMapping::isGroup
122261123.3ms23.3msFoswiki::Users::TopicUserMapping::::CORE:matchFoswiki::Users::TopicUserMapping::CORE:match (opcode)
4011115.9ms36.2msFoswiki::Users::TopicUserMapping::::_cacheUserFoswiki::Users::TopicUserMapping::_cacheUser
11114.8ms15.3msFoswiki::Users::TopicUserMapping::::BEGIN@36Foswiki::Users::TopicUserMapping::BEGIN@36
63118.12ms62.8msFoswiki::Users::TopicUserMapping::::_loadMappingFoswiki::Users::TopicUserMapping::_loadMapping
1116.07ms18.8msFoswiki::Users::TopicUserMapping::::newFoswiki::Users::TopicUserMapping::new
410425.09ms15.5msFoswiki::Users::TopicUserMapping::::login2cUIDFoswiki::Users::TopicUserMapping::login2cUID
114112.18ms89.6msFoswiki::Users::TopicUserMapping::::handlesUserFoswiki::Users::TopicUserMapping::handlesUser
2111.63ms84.2msFoswiki::Users::TopicUserMapping::::_expandUserListFoswiki::Users::TopicUserMapping::_expandUserList
61311.48ms29.2msFoswiki::Users::TopicUserMapping::::_userReallyExistsFoswiki::Users::TopicUserMapping::_userReallyExists
1111.19ms1.32msFoswiki::Users::TopicUserMapping::::BEGIN@35Foswiki::Users::TopicUserMapping::BEGIN@35
1111.17ms1.24msFoswiki::Users::TopicUserMapping::::finishFoswiki::Users::TopicUserMapping::finish
1611597µs4.20msFoswiki::Users::TopicUserMapping::::getWikiNameFoswiki::Users::TopicUserMapping::getWikiName
1932575µs4.75msFoswiki::Users::TopicUserMapping::::getLoginNameFoswiki::Users::TopicUserMapping::getLoginName
4411462µs462µsFoswiki::Users::TopicUserMapping::::_collateGroupsFoswiki::Users::TopicUserMapping::_collateGroups
322291µs91.4msFoswiki::Users::TopicUserMapping::::eachGroupMemberFoswiki::Users::TopicUserMapping::eachGroupMember
311141µs2.12msFoswiki::Users::TopicUserMapping::::findUserByWikiNameFoswiki::Users::TopicUserMapping::findUserByWikiName
211137µs3.36sFoswiki::Users::TopicUserMapping::::_getListOfGroupsFoswiki::Users::TopicUserMapping::_getListOfGroups
5641124µs124µsFoswiki::Users::TopicUserMapping::::CORE:substFoswiki::Users::TopicUserMapping::CORE:subst (opcode)
3521120µs120µsFoswiki::Users::TopicUserMapping::::CORE:regcompFoswiki::Users::TopicUserMapping::CORE:regcomp (opcode)
311119µs1.23msFoswiki::Users::TopicUserMapping::::userExistsFoswiki::Users::TopicUserMapping::userExists
21177µs83.8msFoswiki::Users::TopicUserMapping::::isAdminFoswiki::Users::TopicUserMapping::isAdmin
21177µs3.36sFoswiki::Users::TopicUserMapping::::eachGroupFoswiki::Users::TopicUserMapping::eachGroup
11126µs35µsFoswiki::Users::TopicUserMapping::::BEGIN@213Foswiki::Users::TopicUserMapping::BEGIN@213
11122µs61µsFoswiki::Users::TopicUserMapping::::BEGIN@33Foswiki::Users::TopicUserMapping::BEGIN@33
11122µs22µsFoswiki::Users::TopicUserMapping::::BEGIN@28Foswiki::Users::TopicUserMapping::BEGIN@28
11118µs25µsFoswiki::Users::TopicUserMapping::::BEGIN@218Foswiki::Users::TopicUserMapping::BEGIN@218
11118µs26µsFoswiki::Users::TopicUserMapping::::BEGIN@31Foswiki::Users::TopicUserMapping::BEGIN@31
11118µs37µsFoswiki::Users::TopicUserMapping::::BEGIN@32Foswiki::Users::TopicUserMapping::BEGIN@32
11116µs415µsFoswiki::Users::TopicUserMapping::::BEGIN@34Foswiki::Users::TopicUserMapping::BEGIN@34
1114µs4µsFoswiki::Users::TopicUserMapping::::supportsRegistrationFoswiki::Users::TopicUserMapping::supportsRegistration
0000s0sFoswiki::Users::TopicUserMapping::::__ANON__[:439]Foswiki::Users::TopicUserMapping::__ANON__[:439]
0000s0sFoswiki::Users::TopicUserMapping::::__ANON__[:447]Foswiki::Users::TopicUserMapping::__ANON__[:447]
0000s0sFoswiki::Users::TopicUserMapping::::__ANON__[:586]Foswiki::Users::TopicUserMapping::__ANON__[:586]
0000s0sFoswiki::Users::TopicUserMapping::::__ANON__[:737]Foswiki::Users::TopicUserMapping::__ANON__[:737]
0000s0sFoswiki::Users::TopicUserMapping::::_clearGroupCacheFoswiki::Users::TopicUserMapping::_clearGroupCache
0000s0sFoswiki::Users::TopicUserMapping::::_writeGroupTopicFoswiki::Users::TopicUserMapping::_writeGroupTopic
0000s0sFoswiki::Users::TopicUserMapping::::addUserFoswiki::Users::TopicUserMapping::addUser
0000s0sFoswiki::Users::TopicUserMapping::::addUserToGroupFoswiki::Users::TopicUserMapping::addUserToGroup
0000s0sFoswiki::Users::TopicUserMapping::::checkPasswordFoswiki::Users::TopicUserMapping::checkPassword
0000s0sFoswiki::Users::TopicUserMapping::::eachMembershipFoswiki::Users::TopicUserMapping::eachMembership
0000s0sFoswiki::Users::TopicUserMapping::::eachUserFoswiki::Users::TopicUserMapping::eachUser
0000s0sFoswiki::Users::TopicUserMapping::::findUserByEmailFoswiki::Users::TopicUserMapping::findUserByEmail
0000s0sFoswiki::Users::TopicUserMapping::::getEmailsFoswiki::Users::TopicUserMapping::getEmails
0000s0sFoswiki::Users::TopicUserMapping::::groupAllowsChangeFoswiki::Users::TopicUserMapping::groupAllowsChange
0000s0sFoswiki::Users::TopicUserMapping::::groupAllowsViewFoswiki::Users::TopicUserMapping::groupAllowsView
0000s0sFoswiki::Users::TopicUserMapping::::mapper_getEmailsFoswiki::Users::TopicUserMapping::mapper_getEmails
0000s0sFoswiki::Users::TopicUserMapping::::mapper_setEmailsFoswiki::Users::TopicUserMapping::mapper_setEmails
0000s0sFoswiki::Users::TopicUserMapping::::passwordErrorFoswiki::Users::TopicUserMapping::passwordError
0000s0sFoswiki::Users::TopicUserMapping::::removeUserFoswiki::Users::TopicUserMapping::removeUser
0000s0sFoswiki::Users::TopicUserMapping::::removeUserFromGroupFoswiki::Users::TopicUserMapping::removeUserFromGroup
0000s0sFoswiki::Users::TopicUserMapping::::setEmailsFoswiki::Users::TopicUserMapping::setEmails
0000s0sFoswiki::Users::TopicUserMapping::::setPasswordFoswiki::Users::TopicUserMapping::setPassword
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for license and copyright information
2
3=begin TML
4
5---+ package Foswiki::Users::TopicUserMapping @isa Foswiki::UserMapping');
6
7use
8
9The User mapping is the process by which Foswiki maps from a username (a login name)
10to a wikiname and back. It is also where groups are defined.
11
12By default Foswiki maintains user topics and group topics in the %MAINWEB% that
13define users and group. These topics are
14 * !WikiUsers - stores a mapping from usernames to Wiki names
15 * !WikiName - for each user, stores info about the user
16 * !GroupNameGroup - for each group, a topic ending with "Group" stores a list of users who are part of that group.
17
18Many sites will want to override this behaviour, for example to get users and groups from a corporate database.
19
20This class implements the basic Foswiki behaviour using topics to store users,
21but is also designed to be subclassed so that other services can be used.
22
23Subclasses should be named 'XxxxUserMapping' so that configure can find them.
24
25=cut
26
27package Foswiki::Users::TopicUserMapping;
28271µs122µs
# spent 22µs within Foswiki::Users::TopicUserMapping::BEGIN@28 which was called: # once (22µs+0s) by Foswiki::Users::new at line 28
use Foswiki::UserMapping ();
# spent 22µs making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@28
29114µsour @ISA = ('Foswiki::UserMapping');
30
31245µs234µs
# spent 26µs (18+8) within Foswiki::Users::TopicUserMapping::BEGIN@31 which was called: # once (18µs+8µs) by Foswiki::Users::new at line 31
use strict;
# spent 26µs making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@31 # spent 8µs making 1 call to strict::import
32244µs256µs
# spent 37µs (18+19) within Foswiki::Users::TopicUserMapping::BEGIN@32 which was called: # once (18µs+19µs) by Foswiki::Users::new at line 32
use warnings;
# spent 37µs making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@32 # spent 19µs making 1 call to warnings::import
33248µs2101µs
# spent 61µs (22+40) within Foswiki::Users::TopicUserMapping::BEGIN@33 which was called: # once (22µs+40µs) by Foswiki::Users::new at line 33
use Assert;
# spent 61µs making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@33 # spent 40µs making 1 call to Assert::import
34248µs2813µs
# spent 415µs (16+398) within Foswiki::Users::TopicUserMapping::BEGIN@34 which was called: # once (16µs+398µs) by Foswiki::Users::new at line 34
use Error qw( :try );
# spent 415µs making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@34 # spent 398µs making 1 call to Error::import
352161µs11.32ms
# spent 1.32ms (1.19+128µs) within Foswiki::Users::TopicUserMapping::BEGIN@35 which was called: # once (1.19ms+128µs) by Foswiki::Users::new at line 35
use Foswiki::ListIterator ();
# spent 1.32ms making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@35
362971µs115.3ms
# spent 15.3ms (14.8+559µs) within Foswiki::Users::TopicUserMapping::BEGIN@36 which was called: # once (14.8ms+559µs) by Foswiki::Users::new at line 36
use Foswiki::Func ();
# spent 15.3ms making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@36
37
38#use Monitor;
39#Monitor::MonitorMethod('Foswiki::Users::TopicUserMapping');
40
41=begin TML
42
43---++ ClassMethod new ($session, $impl)
44
45Constructs a new user mapping handler of this type, referring to $session
46for any required Foswiki services.
47
48=cut
49
50# The null mapping name is reserved for Foswiki for backward-compatibility.
51# We declare this as a global variable so we can override it during testing.
5212µsour $FOSWIKI_USER_MAPPING_ID = '';
53
54#our $FOSWIKI_USER_MAPPING_ID = 'TestMapping_';
55
56
# spent 18.8ms (6.07+12.7) within Foswiki::Users::TopicUserMapping::new which was called: # once (6.07ms+12.7ms) by Foswiki::Users::new at line 104 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Users.pm
sub new {
5714113µs my ( $class, $session ) = @_;
58
59123µs my $this = $class->SUPER::new( $session, $FOSWIKI_USER_MAPPING_ID );
# spent 23µs making 1 call to Foswiki::UserMapping::new
60
61 my $implPasswordManager = $Foswiki::cfg{PasswordManager};
62 $implPasswordManager = 'Foswiki::Users::Password'
63 if ( $implPasswordManager eq 'none' );
64 eval "require $implPasswordManager";
# spent 129µs executing statements in string eval
65 die $@ if $@;
6619.08ms $this->{passwords} = $implPasswordManager->new($session);
# spent 9.08ms making 1 call to Foswiki::Users::HtPasswdUser::new
67
68# if password manager says sorry, we're read only today
69# 'none' is a special case, as it means we're not actually using the password manager for
70# registration.
711100µs if ( $this->{passwords}->readOnly()
# spent 100µs making 1 call to Foswiki::Users::HtPasswdUser::readOnly
72 && ( $Foswiki::cfg{PasswordManager} ne 'none' )
73 && $Foswiki::cfg{Register}{EnableNewUserRegistration} )
74 {
75 $session->logger->log( 'warning',
76'TopicUserMapping has TURNED OFF EnableNewUserRegistration, because the password file is read only.'
77 );
78 $Foswiki::cfg{Register}{EnableNewUserRegistration} = 0;
79 }
80
81 #SMELL: and this is a second user object
82 #TODO: combine with the one in Foswiki::Users
83 #$this->{U2L} = {};
84 $this->{L2U} = {};
85 $this->{U2W} = {};
86 $this->{W2U} = {};
87 $this->{eachGroupMember} = {};
88 $this->{singleGroupMembers} = ();
89
90 return $this;
91}
92
93=begin TML
94
95---++ ObjectMethod finish()
96Break circular references.
97
98=cut
99
100# Note to developers; please undef *all* fields in the object explicitly,
101# whether they are references or not. That way this method is "golden
102# documentation" of the live fields in the object.
103
# spent 1.24ms (1.17+75µs) within Foswiki::Users::TopicUserMapping::finish which was called: # once (1.17ms+75µs) by Foswiki::Users::finish at line 165 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Users.pm
sub finish {
10491.16ms my $this = shift;
105
106163µs $this->{passwords}->finish() if $this->{passwords};
# spent 63µs making 1 call to Foswiki::Users::HtPasswdUser::finish
107 undef $this->{L2U};
108 undef $this->{U2W};
109 undef $this->{W2U};
110 undef $this->{passwords};
111 undef $this->{eachGroupMember};
112 undef $this->{singleGroupMembers};
113111µs $this->SUPER::finish();
# spent 11µs making 1 call to Foswiki::UserMapping::finish
114}
115
116=begin TML
117
118---++ ObjectMethod supportsRegistration () -> false
119return 1 if the UserMapper supports registration (ie can create new users)
120
121=cut
122
123
# spent 4µs within Foswiki::Users::TopicUserMapping::supportsRegistration which was called: # once (4µs+0s) by Foswiki::Users::supportsRegistration at line 235 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Users.pm
sub supportsRegistration {
12418µs return 1;
125}
126
127=begin TML
128
129---++ ObjectMethod handlesUser ( $cUID, $login, $wikiname) -> $boolean
130
131Called by the Foswiki::Users object to determine which loaded mapping
132to use for a given user.
133
134The user can be identified by any of $cUID, $login or $wikiname. Any of
135these parameters may be undef, and they should be tested in order; cUID
136first, then login, then wikiname. This mapping is special - for backwards
137compatibility, it assumes responsibility for _all_ non BaseMapping users.
138If you're needing to mix the TopicUserMapping with other mappings,
139define $this->{mapping_id} = 'TopicUserMapping_';
140
141=cut
142
143
# spent 89.6ms (2.18+87.4) within Foswiki::Users::TopicUserMapping::handlesUser which was called 114 times, avg 786µs/call: # 114 times (2.18ms+87.4ms) by Foswiki::Users::_getMapping at line 214 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Users.pm, avg 786µs/call
sub handlesUser {
1445061.41ms my ( $this, $cUID, $login, $wikiname ) = @_;
145114248µs if ( defined $cUID && !length( $this->{mapping_id} ) ) {
146
147 # Handle all cUIDs if the mapping ID is not defined
148 return 1;
149 }
150 else {
151
152 # Used when (if) TopicUserMapping is subclassed
153 return 1 if ( defined $cUID && $cUID =~ /^($this->{mapping_id})/ );
154 }
155
156 # Check the login id to see if we know it
1573224.6ms return 1 if ( $login && $this->_userReallyExists($login) );
# spent 24.6ms making 32 calls to Foswiki::Users::TopicUserMapping::_userReallyExists, avg 769µs/call
158
159 # Or the wiki name
160126458µs if ($wikiname) {
1616362.8ms $this->_loadMapping(); # Sorry Sven, has to be done
# spent 62.8ms making 63 calls to Foswiki::Users::TopicUserMapping::_loadMapping, avg 996µs/call
162 return 1 if defined $this->{W2U}->{$wikiname};
163 }
164
165 return 0;
166}
167
168=begin TML
169
170---++ ObjectMethod login2cUID ($login, $dontcheck) -> $cUID
171
172Convert a login name to the corresponding canonical user name. The
173canonical name can be any string of 7-bit alphanumeric and underscore
174characters, and must correspond 1:1 to the login name.
175(undef on failure)
176
177(if dontcheck is true, return a cUID for a nonexistant user too.
178This is used for registration)
179
180=cut
181
182
# spent 15.5ms (5.09+10.4) within Foswiki::Users::TopicUserMapping::login2cUID which was called 410 times, avg 38µs/call: # 400 times (4.86ms+8.53ms) by Foswiki::Users::TopicUserMapping::_cacheUser at line 1494, avg 33µs/call # 5 times (106µs+847µs) by Foswiki::Users::TopicUserMapping::getLoginName at line 221, avg 190µs/call # 3 times (73µs+635µs) by Foswiki::Users::TopicUserMapping::findUserByWikiName at line 1402, avg 236µs/call # 2 times (52µs+382µs) by Foswiki::Users::getCanonicalUserID at line 463 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Users.pm, avg 217µs/call
sub login2cUID {
18312305.03ms my ( $this, $login, $dontcheck ) = @_;
184
1851050µs unless ($dontcheck) {
186101.61ms return unless ( _userReallyExists( $this, $login ) );
# spent 1.61ms making 10 calls to Foswiki::Users::TopicUserMapping::_userReallyExists, avg 161µs/call
187 }
188
1894108.79ms return $this->{mapping_id} . Foswiki::Users::mapLogin2cUID($login);
# spent 8.79ms making 410 calls to Foswiki::Users::mapLogin2cUID, avg 21µs/call
190}
191
192=begin TML
193
194---++ ObjectMethod getLoginName ($cUID) -> login
195
196Converts an internal cUID to that user's login
197(undef on failure)
198
199=cut
200
201
# spent 4.75ms (575µs+4.17) within Foswiki::Users::TopicUserMapping::getLoginName which was called 19 times, avg 250µs/call: # 16 times (443µs+3.03ms) by Foswiki::Users::TopicUserMapping::getWikiName at line 503, avg 217µs/call # 2 times (92µs+739µs) by Foswiki::Users::TopicUserMapping::userExists at line 530, avg 416µs/call # once (40µs+401µs) by Foswiki::Users::getLoginName at line 662 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Users.pm
sub getLoginName {
202124577µs my ( $this, $cUID ) = @_;
2031960µs ASSERT($cUID) if DEBUG;
# spent 60µs making 19 calls to Assert::ASSERTS_OFF, avg 3µs/call
204
205 my $login = $cUID;
206
207 #can't call userExists - its recursive
208 #return unless (userExists($this, $user));
209
210 # Remove the mapping id in case this is a subclass
211 $login =~ s/$this->{mapping_id}// if $this->{mapping_id};
212
213291µs243µs
# spent 35µs (26+8) within Foswiki::Users::TopicUserMapping::BEGIN@213 which was called: # once (26µs+8µs) by Foswiki::Users::new at line 213
use bytes;
# spent 35µs making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@213 # spent 8µs making 1 call to bytes::import
214
215 # Reverse the encoding used to generate cUIDs in login2cUID
216 # use bytes to ignore character encoding
2171950µs $login =~ s/_([0-9a-f][0-9a-f])/chr(hex($1))/gei;
# spent 50µs making 19 calls to Foswiki::Users::TopicUserMapping::CORE:subst, avg 3µs/call
21828.64ms231µs
# spent 25µs (18+7) within Foswiki::Users::TopicUserMapping::BEGIN@218 which was called: # once (18µs+7µs) by Foswiki::Users::new at line 218
no bytes;
# spent 25µs making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@218 # spent 6µs making 1 call to bytes::unimport
219
220192.98ms return unless _userReallyExists( $this, $login );
# spent 2.98ms making 19 calls to Foswiki::Users::TopicUserMapping::_userReallyExists, avg 157µs/call
2215952µs return unless ( $cUID eq $this->login2cUID($login) );
# spent 952µs making 5 calls to Foswiki::Users::TopicUserMapping::login2cUID, avg 190µs/call
222
223 # Validated
2245124µs return Foswiki::Sandbox::untaintUnchecked($login);
# spent 124µs making 5 calls to Foswiki::Sandbox::untaintUnchecked, avg 25µs/call
225}
226
227# test if the login is in the WikiUsers topic, or in the password file
228# depending on the AllowLoginNames setting
229
# spent 29.2ms (1.48+27.7) within Foswiki::Users::TopicUserMapping::_userReallyExists which was called 61 times, avg 479µs/call: # 32 times (754µs+23.9ms) by Foswiki::Users::TopicUserMapping::handlesUser at line 157, avg 769µs/call # 19 times (455µs+2.53ms) by Foswiki::Users::TopicUserMapping::getLoginName at line 220, avg 157µs/call # 10 times (269µs+1.34ms) by Foswiki::Users::TopicUserMapping::login2cUID at line 186, avg 161µs/call
sub _userReallyExists {
230183575µs my ( $this, $login ) = @_;
231
232 if ( $Foswiki::cfg{Register}{AllowLoginName}
233 || $Foswiki::cfg{PasswordManager} eq 'none' )
234 {
235
236 # need to use the WikiUsers file
237 $this->_loadMapping();
238 return 1 if ( defined( $this->{L2U}->{$login} ) );
239 }
240
241156764µs61229µs if ( $this->{passwords}->canFetchUsers() ) {
# spent 229µs making 61 calls to Foswiki::Users::HtPasswdUser::canFetchUsers, avg 4µs/call
242
243 # AllowLoginName mapping failed, maybe the user is however
244 # present in the Wiki managed pwd file
245 # can use the password file if available
2466127.5ms my $pass = $this->{passwords}->fetchPass($login);
# spent 27.5ms making 61 calls to Foswiki::Users::HtPasswdUser::fetchPass, avg 451µs/call
247 return unless ( defined($pass) );
248 return if ( $pass eq '0' ); # login invalid... (SMELL: what
249 # does that really mean)
250 return 1;
251 }
252 else {
253 return 0;
254 }
255
256 return 0;
257}
258
259=begin TML
260
261---++ ObjectMethod addUser ($login, $wikiname, $password, $emails) -> $cUID
262
263throws an Error::Simple
264
265Add a user to the persistant mapping that maps from usernames to wikinames
266and vice-versa. The default implementation uses a special topic called
267"WikiUsers" in the users web. Subclasses will provide other implementations
268(usually stubs if they have other ways of mapping usernames to wikinames).
269Names must be acceptable to $Foswiki::cfg{NameFilter}
270$login must *always* be specified. $wikiname may be undef, in which case
271the user mapper should make one up.
272This function must return a *canonical user id* that it uses to uniquely
273identify the user. This can be the login name, or the wikiname if they
274are all guaranteed unigue, or some other string consisting only of 7-bit
275alphanumerics and underscores.
276if you fail to create a new user (for eg your Mapper has read only access),
277 throw Error::Simple(
278 'Failed to add user: '.$ph->error());
279
280=cut
281
282sub addUser {
283 my ( $this, $login, $wikiname, $password, $emails ) = @_;
284
285 ASSERT($login) if DEBUG;
286
287 # SMELL: really ought to be smarter about this e.g. make a wikiword
288 $wikiname ||= $login;
289
290 if ( $this->{passwords}->fetchPass($login) ) {
291
292 # They exist; their password must match
293 unless ( $this->{passwords}->checkPassword( $login, $password ) ) {
294 throw Error::Simple(
295 'New password did not match existing password for this user');
296 }
297
298 # User exists, and the password was good.
299 }
300 else {
301
302 # add a new user
303
304 unless ( defined($password) ) {
305 require Foswiki::Users;
306 $password = Foswiki::Users::randomPassword();
307 }
308
309 unless ( $this->{passwords}->setPassword( $login, $password ) == 1 ) {
310
311 throw Error::Simple(
312 'Failed to add user: ' . $this->{passwords}->error() );
313 }
314 }
315
316 my $usersTopicObject;
317
318 if (
319 $this->{session}->topicExists(
320 $Foswiki::cfg{UsersWebName},
321 $Foswiki::cfg{UsersTopicName}
322 )
323 )
324 {
325
326 # Load existing users topic
327 $usersTopicObject = Foswiki::Meta->load(
328 $this->{session},
329 $Foswiki::cfg{UsersWebName},
330 $Foswiki::cfg{UsersTopicName}
331 );
332 }
333 else {
334
335 # Construct a new users topic from the template
336 my $templateTopicObject =
337 Foswiki::Meta->load( $this->{session}, $Foswiki::cfg{SystemWebName},
338 'UsersTemplate' );
339 $usersTopicObject = Foswiki::Meta->new(
340 $this->{session}, $Foswiki::cfg{UsersWebName},
341 $Foswiki::cfg{UsersTopicName}, $templateTopicObject->text()
342 );
343
344 $usersTopicObject->copyFrom($templateTopicObject);
345 }
346
347 my $entry = " * $wikiname - ";
348 $entry .= $login . " - " if $login;
349
350 require Foswiki::Time;
351 my $today =
352 Foswiki::Time::formatTime( time(), $Foswiki::cfg{DefaultDateFormat},
353 'gmtime' );
354
355 # add to the mapping caches
356 my $user = _cacheUser( $this, $wikiname, $login );
357 ASSERT($user) if DEBUG;
358
359 # add name alphabetically to list
360
361 # insidelist is used to see if we are before the first record or after the last
362 # 0 before, 1 inside, 2 after
363 my $insidelist = 0;
364 my $input = $usersTopicObject->text();
365 my $output = '';
366 foreach my $line ( split( /\r?\n/, $input || '' ) ) {
367
368 # TODO: I18N fix here once basic auth problem with 8-bit user names is
369 # solved
370 if ($entry) {
371 my ( $web, $name, $odate ) = ( '', '', '' );
372 if ( $line =~
373/^\s+\*\s($Foswiki::regex{webNameRegex}\.)?($Foswiki::regex{wikiWordRegex})\s*(?:-\s*\w+\s*)?-\s*(.*)/
374 )
375 {
376 $web = $1 || $Foswiki::cfg{UsersWebName};
377 $name = $2;
378 $odate = $3;
379
380 # Filter-in date format matching {DefaultDateFormat}
381 # The admin may have changed the format at some point of time
382 # so we test with a generic format that matches all 4 formats.
383 $odate = ''
384 unless $odate =~ /^\d+[- .\/]+[A-Za-z0-9]+[- .\/]+\d+$/;
385 $insidelist = 1;
386 }
387 elsif ( $line =~ /^\s+\*\s([A-Z]) - / ) {
388
389 # * A - <a name="A">- - - -</a>^M
390 $name = $1;
391 $insidelist = 1;
392 }
393 elsif ( $insidelist == 1 ) {
394
395 # After last entry we have a blank line or some comment
396 # We assume no blank lines inside the list of users
397 # We cannot look for last after Z because Z is not the last letter
398 # in all alphabets
399 $insidelist = 2;
400 $name = '';
401 }
402 if ( ( $name && ( $wikiname le $name ) ) || $insidelist == 2 ) {
403
404 # found alphabetical position or last record
405 if ( $wikiname eq $name ) {
406
407 # adjusting existing user - keep original registration date
408 $entry .= $odate;
409 }
410 else {
411 $entry .= $today . "\n" . $line;
412 }
413
414 # don't adjust if unchanged
415 return $user if ( $entry eq $line );
416 $line = $entry;
417 $entry = '';
418 }
419 }
420
421 $output .= $line . "\n";
422 }
423 if ($entry) {
424
425 # brand new file - add to end
426 $output .= "$entry$today\n";
427 }
428 $usersTopicObject->text($output);
429
430 $this->{CACHED} = 0;
431 try {
432 $usersTopicObject->save(
433 author =>
434
435 # SMELL: why is this Admin and not the RegoAgent??
436 $this->{session}->{users}
437 ->getCanonicalUserID( $Foswiki::cfg{AdminUserLogin} )
438 );
439 }
440 catch Error::Simple with {
441
442 # Failed to add user; must remove them from the password system too,
443 # otherwise their next registration attempt will be blocked
444 my $e = shift;
445 $this->{passwords}->removeUser($login);
446 throw $e;
447 };
448
449#can't call setEmails here - user may be in the process of being registered
450#TODO; when registration is moved into the mapping, setEmails will happend after the createUserTOpic
451#$this->setEmails( $user, $emails );
452
453 return $user;
454}
455
456=begin TML
457
458---++ ObjectMethod removeUser( $cUID ) -> $boolean
459
460Delete the users entry. Removes the user from the password
461manager and user mapping manager. Does *not* remove their personal
462topics, which may still be linked.
463
464=cut
465
466sub removeUser {
467 my ( $this, $cUID ) = @_;
468 my $ln = $this->getLoginName($cUID);
469 $this->{passwords}->removeUser($ln);
470
471 # SMELL: does not update the internal caches,
472 # needs someone to implement it
473}
474
475=begin TML
476
477---++ ObjectMethod getWikiName ($cUID) -> $wikiname
478
479Map a canonical user name to a wikiname. If it fails to find a
480WikiName, it will attempt to find a matching loginname, and use
481an escaped version of that.
482If there is no matching WikiName or LoginName, it returns undef.
483
484=cut
485
486
# spent 4.20ms (597µs+3.61) within Foswiki::Users::TopicUserMapping::getWikiName which was called 16 times, avg 263µs/call: # 16 times (597µs+3.61ms) by Foswiki::Users::getWikiName at line 692 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Users.pm, avg 263µs/call
sub getWikiName {
487112371µs my ( $this, $cUID ) = @_;
4881653µs ASSERT($cUID) if DEBUG;
# spent 53µs making 16 calls to Assert::ASSERTS_OFF, avg 3µs/call
4891650µs ASSERT( $cUID =~ /^$this->{mapping_id}/ ) if DEBUG;
# spent 50µs making 16 calls to Assert::ASSERTS_OFF, avg 3µs/call
490
491 my $wikiname;
492
493 if ( $Foswiki::cfg{Register}{AllowLoginName} ) {
494 $this->_loadMapping();
495 $wikiname = $this->{U2W}->{$cUID};
496 }
497 else {
498
499 # If the mapping isn't enabled there's no point in loading it
500 }
501
50232131µs unless ($wikiname) {
503163.47ms $wikiname = $this->getLoginName($cUID);
# spent 3.47ms making 16 calls to Foswiki::Users::TopicUserMapping::getLoginName, avg 217µs/call
504254µs if ($wikiname) {
505
506 # sanitise the generated WikiName
507430µs $wikiname =~ s/$Foswiki::cfg{NameFilter}//go;
# spent 24µs making 2 calls to Foswiki::Users::TopicUserMapping::CORE:regcomp, avg 12µs/call # spent 6µs making 2 calls to Foswiki::Users::TopicUserMapping::CORE:subst, avg 3µs/call
508 }
509 }
510
511 return $wikiname;
512}
513
514=begin TML
515
516---++ ObjectMethod userExists($cUID) -> $boolean
517
518Determine if the user already exists or not. Whether a user exists
519or not is determined by the password manager.
520
521=cut
522
523
# spent 1.23ms (119µs+1.11) within Foswiki::Users::TopicUserMapping::userExists which was called 3 times, avg 409µs/call: # 3 times (119µs+1.11ms) by Foswiki::Users::TopicUserMapping::findUserByWikiName at line 1407, avg 409µs/call
sub userExists {
5241999µs my ( $this, $cUID ) = @_;
525310µs ASSERT($cUID) if DEBUG;
# spent 10µs making 3 calls to Assert::ASSERTS_OFF, avg 3µs/call
526
527 # Do this to avoid a password manager lookup
528 return 1 if $cUID eq $this->{session}->{user};
529
5302831µs my $loginName = $this->getLoginName($cUID);
# spent 831µs making 2 calls to Foswiki::Users::TopicUserMapping::getLoginName, avg 416µs/call
531 return 0 unless defined($loginName);
532
533 return 1 if ( $loginName eq $Foswiki::cfg{DefaultUserLogin} );
534
535 # Foswiki allows *groups* to log in
536225µs return 1 if ( $this->isGroup($loginName) );
# spent 25µs making 2 calls to Foswiki::Users::TopicUserMapping::isGroup, avg 13µs/call
537
538 # Look them up in the password manager (can be slow).
5394243µs return 1
# spent 236µs making 2 calls to Foswiki::Users::HtPasswdUser::fetchPass, avg 118µs/call # spent 7µs making 2 calls to Foswiki::Users::HtPasswdUser::canFetchUsers, avg 4µs/call
540 if ( $this->{passwords}->canFetchUsers()
541 && $this->{passwords}->fetchPass($loginName) );
542
543 unless ( $Foswiki::cfg{Register}{AllowLoginName}
544 || $this->{passwords}->canFetchUsers() )
545 {
546
547 #if there is no pwd file, then its external auth
548 #and if AllowLoginName is also off, then the only way to know if
549 #the user has registered is to test for user topic?
550 if (
551 Foswiki::Func::topicExists(
552 $Foswiki::cfg{UsersWebName}, $loginName
553 )
554 )
555 {
556 return 1;
557 }
558 }
559
560 return 0;
561}
562
563=begin TML
564
565---++ ObjectMethod eachUser () -> Foswiki::Iterator of cUIDs
566
567See baseclass for documentation
568
569=cut
570
571sub eachUser {
572 my ($this) = @_;
573
574 $this->_loadMapping();
575 my @list = keys( %{ $this->{U2W} } );
576 my $iter = new Foswiki::ListIterator( \@list );
577 $iter->{filter} = sub {
578
579 # don't claim users that are handled by the basemapping
580 my $cUID = $_[0] || '';
581 my $login = $this->{session}->{users}->getLoginName($cUID);
582 my $wikiname = $this->{session}->{users}->getWikiName($cUID);
583
584 return !( $this->{session}->{users}->{basemapping}
585 ->handlesUser( undef, $login, $wikiname ) );
586 };
587 return $iter;
588}
589
590=begin TML
591
592---++ ObjectMethod eachGroupMember ($group) -> listIterator of cUIDs
593
594See baseclass for documentation
595
596=cut
597
59811µsmy %expanding; # Prevents loops in nested groups
599
600
# spent 91.4ms (291µs+91.1) within Foswiki::Users::TopicUserMapping::eachGroupMember which was called 3 times, avg 30.5ms/call: # 2 times (166µs+9.64ms) by Foswiki::Users::eachGroupMember at line 802 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Users.pm, avg 4.90ms/call # once (125µs+81.5ms) by Foswiki::UserMapping::isInGroup at line 401 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/UserMapping.pm
sub eachGroupMember {
60132181µs my ( $this, $group, $options ) = @_;
602
603 my $expand = $options->{expand};
604
605310µs if ( Scalar::Util::tainted($group) ) {
# spent 10µs making 3 calls to Scalar::Util::tainted, avg 3µs/call
606 $group = Foswiki::Sandbox::untaint( $group,
607 \&Foswiki::Sandbox::validateTopicName );
608 }
609
610 $expand = 1 unless ( defined $expand );
611
612 # print STDERR "eachGroupMember called for $group - expand $expand \n";
613
614 if ( !$expand && defined( $this->{singleGroupMembers}->{$group} ) ) {
615
616 # print STDERR "Returning cached unexpanded list for $group\n";
617 return new Foswiki::ListIterator(
618 $this->{singleGroupMembers}->{$group} );
619 }
620
621113µs if ( $expand && defined( $this->{eachGroupMember}->{$group} ) ) {
622
623 # print STDERR "Returning cached expanded list for $group\n";
624122µs return new Foswiki::ListIterator( $this->{eachGroupMember}->{$group} );
# spent 22µs making 1 call to Foswiki::ListIterator::new
625 }
626
627 # print "Cache miss for $group expand $expand \n";
628
629 my $session = $this->{session};
630 my $users = $session->{users};
631
632 my $members = [];
633 my $singleGroupMembers = [];
634
635# Determine if we are called recursively, either directly, or by the _expandUserList routine
63625µs unless ( ( caller(1) )[3] eq ( caller(0) )[3]
637 || ( caller(2) )[3] eq ( caller(0) )[3] )
638 {
639
640 # print "eachGroupMember $group - TOP LEVEL \n";
641 %expanding = ();
642 }
643
644843µs2357µs if ( !$expanding{$group}
# spent 357µs making 2 calls to Foswiki::topicExists, avg 179µs/call
645 && $session->topicExists( $Foswiki::cfg{UsersWebName}, $group ) )
646 {
647 $expanding{$group} = 1;
648
649 # print "Expanding $group \n";
65024.85ms my $groupTopicObject =
# spent 4.85ms making 2 calls to Foswiki::Meta::load, avg 2.43ms/call
651 Foswiki::Meta->load( $this->{session}, $Foswiki::cfg{UsersWebName},
652 $group );
653
654446µs if ( !$expand ) {
655 $singleGroupMembers =
656 _expandUserList( $this, $groupTopicObject->getPreference('GROUP'),
657 0 );
658 $this->{singleGroupMembers}->{$group} = $singleGroupMembers;
659
660# print "Returning iterator for singleGroupMembers $group, members $singleGroupMembers \n";
661 return new Foswiki::ListIterator(
662 $this->{singleGroupMembers}->{$group} );
663 }
664 else {
665485.8ms $members =
# spent 84.2ms making 2 calls to Foswiki::Users::TopicUserMapping::_expandUserList, avg 42.1ms/call # spent 1.59ms making 2 calls to Foswiki::Meta::getPreference, avg 796µs/call
666 _expandUserList( $this,
667 $groupTopicObject->getPreference('GROUP') );
668 $this->{eachGroupMember}->{$group} = $members;
669 }
670
671 delete $expanding{$group};
672 }
673
674 # print "Returning iterator for eachGroupMember $group \n";
675252µs return new Foswiki::ListIterator( $this->{eachGroupMember}->{$group} );
# spent 52µs making 2 calls to Foswiki::ListIterator::new, avg 26µs/call
676}
677
678=begin TML
679
680---++ ObjectMethod isGroup ($user) -> boolean
681
682See baseclass for documentation
683
684=cut
685
686
# spent 3.53s (143ms+3.39) within Foswiki::Users::TopicUserMapping::isGroup which was called 12226 times, avg 289µs/call: # 12164 times (143ms+3.39s) by Foswiki::Users::isGroup at line 821 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Users.pm, avg 290µs/call # 33 times (384µs+62µs) by Foswiki::Users::TopicUserMapping::_expandUserList at line 1626, avg 14µs/call # 24 times (280µs+42µs) by Foswiki::UserMapping::isInGroup at line 408 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/UserMapping.pm, avg 13µs/call # 3 times (32µs+6µs) by Foswiki::Users::TopicUserMapping::findUserByWikiName at line 1370, avg 12µs/call # 2 times (21µs+4µs) by Foswiki::Users::TopicUserMapping::userExists at line 536, avg 13µs/call
sub isGroup {
68736682176ms my ( $this, $user ) = @_;
688
689 # Groups have the same username as wikiname as canonical name
690 return 1 if $user eq $Foswiki::cfg{SuperAdminGroup};
691
6921222623.3ms return 0 unless ( $user =~ /Group$/ );
# spent 23.3ms making 12226 calls to Foswiki::Users::TopicUserMapping::CORE:match, avg 2µs/call
693
694 #actually test for the existance of this group
695 #TODO: SMELL: this is still a lie, because it will claim that a
696 #Group which the currently logged in user does _not_
697 #have VIEW permission for simply is non-existant.
698 #however, this may be desirable for security reasons.
699 #SMELL: this is why we should not use topicExist to test for createability...
70023.36s my $iterator = $this->eachGroup();
# spent 3.36s making 2 calls to Foswiki::Users::TopicUserMapping::eachGroup, avg 1.68s/call
701260µs while ( $iterator->hasNext() ) {
# spent 60µs making 2 calls to Foswiki::ListIterator::hasNext, avg 30µs/call
70256344µs28640µs my $groupname = $iterator->next();
# spent 640µs making 28 calls to Foswiki::ListIterator::next, avg 23µs/call
70326578µs return 1 if ( $groupname eq $user );
# spent 578µs making 26 calls to Foswiki::ListIterator::hasNext, avg 22µs/call
704 }
705 return 0;
706}
707
708=begin TML
709
710---++ ObjectMethod eachGroup () -> ListIterator of groupnames
711
712See baseclass for documentation
713
714=cut
715
716
# spent 3.36s (77µs+3.36) within Foswiki::Users::TopicUserMapping::eachGroup which was called 2 times, avg 1.68s/call: # 2 times (77µs+3.36s) by Foswiki::Users::TopicUserMapping::isGroup at line 700, avg 1.68s/call
sub eachGroup {
717679µs my ($this) = @_;
71823.36s _getListOfGroups($this);
# spent 3.36s making 2 calls to Foswiki::Users::TopicUserMapping::_getListOfGroups, avg 1.68s/call
71924µs277µs return new Foswiki::ListIterator( \@{ $this->{groupsList} } );
# spent 77µs making 2 calls to Foswiki::ListIterator::new, avg 38µs/call
720}
721
722=begin TML
723
724---++ ObjectMethod eachMembership ($cUID) -> ListIterator of groups this user is in
725
726See baseclass for documentation
727
728=cut
729
730sub eachMembership {
731 my ( $this, $user ) = @_;
732
733 _getListOfGroups($this);
734 my $it = new Foswiki::ListIterator( \@{ $this->{groupsList} } );
735 $it->{filter} = sub {
736 $this->isInGroup( $user, $_[0] );
737 };
738 return $it;
739}
740
741=begin TML
742
743---++ ObjectMethod groupAllowsView($group) -> boolean
744
745returns 1 if the group is able to be viewed by the current logged in user
746
747implemented using topic VIEW permissions
748
749=cut
750
751sub groupAllowsView {
752 my $this = shift;
753 my $Group = shift;
754
755 my $user = $this->{session}->{user};
756 return 1 if $this->{session}->{users}->isAdmin($user);
757
758 $Group = Foswiki::Sandbox::untaint( $Group,
759 \&Foswiki::Sandbox::validateTopicName );
760 my ( $groupWeb, $groupName ) =
761 $this->{session}
762 ->normalizeWebTopicName( $Foswiki::cfg{UsersWebName}, $Group );
763
764# If a Group or User topic normalized somewhere else, doesn't make sense, so ignore the Webname
765 $groupWeb = $Foswiki::cfg{UsersWebName};
766
767 $groupName = undef
768 if ( not $this->{session}->topicExists( $groupWeb, $groupName ) );
769
770 return Foswiki::Func::checkAccessPermission( 'VIEW', $user, undef,
771 $groupName, $groupWeb );
772}
773
774=begin TML
775
776---++ ObjectMethod groupAllowsChange($group, $cuid) -> boolean
777
778returns 1 if the group is able to be modified by $cuid
779
780implemented using topic CHANGE permissions
781
782=cut
783
784sub groupAllowsChange {
785 my $this = shift;
786 my $Group = shift;
787 my $user = shift;
788 ASSERT( defined $user ) if DEBUG;
789
790 return 1 if $this->{session}->{users}->isAdmin($user);
791
792 $Group = Foswiki::Sandbox::untaint( $Group,
793 \&Foswiki::Sandbox::validateTopicName );
794 my ( $groupWeb, $groupName ) =
795 $this->{session}
796 ->normalizeWebTopicName( $Foswiki::cfg{UsersWebName}, $Group );
797
798# If a Group or User topic normalized somewhere else, doesn't make sense, so ignore the Webname
799 $groupWeb = $Foswiki::cfg{UsersWebName};
800
801 $groupName = undef
802 if ( not $this->{session}->topicExists( $groupWeb, $groupName ) );
803
804 return Foswiki::Func::checkAccessPermission( 'CHANGE', $user, undef,
805 $groupName, $groupWeb );
806}
807
808=begin TML
809
810---++ ObjectMethod addToGroup( $cuid, $group, $create ) -> $boolean
811adds the user specified by the cuid to the group.
812If the group does not exist, it will return false and do nothing, unless the create flag is set.
813
814cuid be a groupname which is added like it was an unknown user
815
816=cut
817
818sub addUserToGroup {
819 my ( $this, $cuid, $Group, $create ) = @_;
820 $Group = Foswiki::Sandbox::untaint( $Group,
821 \&Foswiki::Sandbox::validateTopicName );
822 my ( $groupWeb, $groupName ) =
823 $this->{session}
824 ->normalizeWebTopicName( $Foswiki::cfg{UsersWebName}, $Group );
825
826 throw Error::Simple("Users cannot be added to $Group")
827 if ( $Group eq 'NobodyGroup' || $Group eq 'BaseGroup' );
828
829 throw Error::Simple('Group names must end in Group')
830 unless ( $Group =~ m/Group$/ );
831
832 # the registration code will call this function using the rego agent
833 my $user = $this->{session}->{user};
834
835 my $usersObj = $this->{session}->{users};
836
837 print STDERR "$user, aka("
838 . $usersObj->getWikiName($user)
839 . ") is TRYING to add $cuid aka("
840 . $usersObj->getWikiName($cuid)
841 . ") to $groupName\n"
842 if DEBUG;
843
844 my $membersString = '';
845 my $allowChangeString;
846 my $groupTopicObject;
847
848 if ( $usersObj->isGroup($groupName) ) {
849
850 $groupTopicObject =
851 Foswiki::Meta->load( $this->{session}, $groupWeb, $groupName );
852
853 if ( !$groupTopicObject->haveAccess( 'CHANGE', $user ) ) {
854 return 0;
855
856 #throw Error::Simple(
857 # "CHANGE not permitted by $user");
858 }
859
860 $membersString = $groupTopicObject->getPreference('GROUP') || '';
861
862 my @l;
863 foreach my $ident ( split( /[\,\s]+/, $membersString ) ) {
864 $ident =~ s/^($Foswiki::cfg{UsersWebName}|%USERSWEB%|%MAINWEB%)\.//;
865 push( @l, $ident ) if $ident;
866 }
867 $membersString = join( ', ', @l );
868
869 if ( $create and !defined($cuid) ) {
870
871 #upgrade group topic.
872 $this->_writeGroupTopic(
873 $groupTopicObject, $groupWeb, $groupName,
874 $membersString, $allowChangeString
875 );
876
877 return 1;
878 }
879 }
880 else {
881
882# see if we have permission to add a topic, or to edit the existing topic, etc..
883
884 #throw Error::Simple(
885 # 'Group does not exist and create not permitted')
886 return 0
887 unless ($create);
888
889 #throw Error::Simple(
890 # "CHANGE not permitted for $groupName by $user")
891 return 0
892 unless (
893 Foswiki::Func::checkAccessPermission(
894 'CHANGE', $user, '', $groupName, $groupWeb
895 )
896 );
897
898 $groupTopicObject =
899 Foswiki::Meta->load( $this->{session}, $groupWeb, 'GroupTemplate' );
900
901 # expand the GroupTemplate as best we can.
902 $this->{session}->{request}
903 ->param( -name => 'topic', -value => $groupName );
904 $groupTopicObject->expandNewTopic();
905
906 $allowChangeString = $groupName;
907 }
908
909 my $wikiName = $usersObj->getWikiName($cuid);
910
911 if ( $membersString !~ m/$wikiName/ ) {
912 $membersString .= ', ' if ( $membersString ne '' );
913 $membersString .= $wikiName;
914 }
915
916 $this->_clearGroupCache($groupName);
917
918 $this->_writeGroupTopic(
919 $groupTopicObject, $groupWeb, $groupName,
920 $membersString, $allowChangeString
921 );
922
923 # reparse groups brute force :/
924 _getListOfGroups( $this, 1 ) if ($create);
925 return 1;
926}
927
928#start by just writing the new form.
929sub _writeGroupTopic {
930 my $this = shift;
931 my $groupTopicObject = shift;
932 my $groupWeb = shift;
933 my $groupName = shift;
934 my $membersString = shift;
935 my $allowChangeString = shift;
936
937 my $text = $groupTopicObject->text() || '';
938
939#TODO: do an attempt to convert existing old style topics - compare to 'normal' GroupTemplate? (I'm hoping to keep any user added descriptions for the group
940 if (
941 (
942 !defined $groupTopicObject->getPreference('VIEW_TEMPLATE')
943 or $groupTopicObject->getPreference('VIEW_TEMPLATE') ne 'GroupView'
944 )
945 or ( $text =~ /^---\+!! <nop>.*$/ )
946 or ( $text =~ /^(\t| )+\* Set GROUP = .*$/ )
947 or ( $text =~ /^(\t| )+\* Member list \(comma-separated list\):$/ )
948 or ( $text =~ /^(\t| )+\* Persons\/group who can change the list:$/ )
949 or ( $text =~ /^(\t| )+\* Set ALLOWTOPICCHANGE = .*$/ )
950 or ( $text =~ /^\*%MAKETEXT{"Related topics:"}%.*$/ )
951 )
952 {
953 if ( !defined($allowChangeString) ) {
954 $allowChangeString =
955 $groupTopicObject->getPreference('ALLOWTOPICCHANGE') || '';
956 }
957
958 $text =~ s/^---\+!! <nop>.*$//s;
959 $text =~ s/^(\t| )+\* Set GROUP = .*$//s;
960 $text =~ s/^(\t| )+\* Member list \(comma-separated list\):$//s;
961 $text =~ s/^(\t| )+\* Persons\/group who can change the list:$//s;
962 $text =~ s/^(\t| )+\* Set ALLOWTOPICCHANGE = .*$//s;
963 $text =~ s/^\*%MAKETEXT{"Related topics:"}%.*$//s;
964
965 $text .= "\nEdit this topic to add a description to the $groupName\n";
966
967#TODO: consider removing the VIEW_TEMPLATE that only very few people should ever have...
968 }
969
970 $groupTopicObject->text($text);
971
972 $groupTopicObject->putKeyed(
973 'PREFERENCE',
974 {
975 type => 'Set',
976 name => 'GROUP',
977 title => 'GROUP',
978 value => $membersString
979 }
980 );
981 if ( defined($allowChangeString) ) {
982 $groupTopicObject->putKeyed(
983 'PREFERENCE',
984 {
985 type => 'Set',
986 name => 'ALLOWTOPICCHANGE',
987 title => 'ALLOWTOPICCHANGE',
988 value => $allowChangeString
989 }
990 );
991 }
992 $groupTopicObject->putKeyed(
993 'PREFERENCE',
994 {
995 type => 'Set',
996 name => 'VIEW_TEMPLATE',
997 title => 'VIEW_TEMPLATE',
998 value => 'GroupView'
999 }
1000 );
1001
1002 #TODO: should also consider securing the new topic?
1003 my $user = $this->{session}->{user};
1004 $groupTopicObject->saveAs(
1005 $groupWeb, $groupName,
1006 author => $user,
1007 forcenewrevision => 0
1008 );
1009
1010}
1011
1012=begin TML
1013
1014---++ ObjectMethod removeFromGroup( $cuid, $group ) -> $boolean
1015
1016=cut
1017
1018sub removeUserFromGroup {
1019 my ( $this, $cuid, $groupName ) = @_;
1020 $groupName = Foswiki::Sandbox::untaint( $groupName,
1021 \&Foswiki::Sandbox::validateTopicName );
1022 my ( $groupWeb, $groupTopic ) =
1023 $this->{session}
1024 ->normalizeWebTopicName( $Foswiki::cfg{UsersWebName}, $groupName );
1025
1026 throw Error::Simple("Users cannot be removed from $groupName")
1027 if ( $groupName eq 'BaseGroup' );
1028
1029 throw Error::Simple("AdminUser cannot be removed from $groupName")
1030 if ( $groupName eq "$Foswiki::cfg{SuperAdminGroup}"
1031 && $cuid eq 'BaseUserMapping_333' );
1032
1033 my $user = $this->{session}->{user};
1034 my $usersObj = $this->{session}->{users};
1035
1036 if (
1037 $usersObj->isGroup($groupName)
1038 and ( $this->{session}
1039 ->topicExists( $Foswiki::cfg{UsersWebName}, $groupName ) )
1040 )
1041 {
1042 if ( !$usersObj->isInGroup( $cuid, $groupName, { expand => 0 } )
1043 && !$usersObj->isGroup($cuid) )
1044 {
1045
1046 #throw Error::Simple(
1047 # "User $cuid not in group, cannot be removed")
1048 return 0;
1049 }
1050 my $groupTopicObject =
1051 Foswiki::Meta->load( $this->{session}, $Foswiki::cfg{UsersWebName},
1052 $groupName );
1053 if ( !$groupTopicObject->haveAccess( 'CHANGE', $user ) ) {
1054
1055 #throw Error::Simple(
1056 # "CHANGE by $user not permitted.")
1057 return 0;
1058 }
1059
1060 my $WikiName = $usersObj->getWikiName($cuid);
1061 my $LoginName = $usersObj->getLoginName($cuid) || '';
1062
1063 my $membersString = $groupTopicObject->getPreference('GROUP');
1064 my @l;
1065 foreach my $ident ( split( /[\,\s]+/, $membersString ) ) {
1066 $ident =~ s/^($Foswiki::cfg{UsersWebName}|%USERSWEB%|%MAINWEB%)\.//;
1067 next if ( $ident eq $WikiName );
1068 next if ( $ident eq $LoginName );
1069 next if ( $ident eq $cuid );
1070 push( @l, $ident );
1071 }
1072 $membersString = join( ', ', @l );
1073
1074 $this->_writeGroupTopic( $groupTopicObject, $groupWeb, $groupTopic,
1075 $membersString );
1076
1077 $this->_clearGroupCache($groupName);
1078
1079 return 1;
1080 }
1081
1082 return 0;
1083}
1084
1085=begin TML
1086
1087---++ ObjectMethod _clearGroupCache( $groupName )
1088
1089Removes the cache entries for unexpanded and expanded groups,
1090and searches un-expanded groups for any nesting group references
1091clearing them as well.
1092
1093Note: This is not recursive and does not attempt to handle
1094more than one level of nested groups.
1095
1096=cut
1097
1098sub _clearGroupCache {
1099 my ( $this, $groupName ) = @_;
1100
1101 delete $this->{eachGroupMember}->{$groupName};
1102 delete $this->{singleGroupMembers}->{$groupName};
1103
1104 #SMELL: This should probably be recursive.
1105 foreach my $groupKey ( keys( %{ $this->{singleGroupMembers} } ) ) {
1106 if ( $this->{singleGroupMembers}->{$groupKey} =~ m/$groupName/ ) {
1107
1108 # print STDERR "Deleting cache for $groupKey \n";
1109 delete $this->{eachGroupMember}->{$groupKey};
1110 delete $this->{singleGroupMembers}->{$groupKey};
1111 }
1112 }
1113}
1114
1115=begin TML
1116
1117---++ ObjectMethod isAdmin( $cUID ) -> $boolean
1118
1119True if the user is an admin
1120 * is $Foswiki::cfg{SuperAdminGroup}
1121 * is a member of the $Foswiki::cfg{SuperAdminGroup}
1122
1123=cut
1124
1125
# spent 83.8ms (77µs+83.7) within Foswiki::Users::TopicUserMapping::isAdmin which was called 2 times, avg 41.9ms/call: # 2 times (77µs+83.7ms) by Foswiki::Users::isAdmin at line 608 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Users.pm, avg 41.9ms/call
sub isAdmin {
1126837µs my ( $this, $cUID ) = @_;
1127 my $isAdmin = 0;
1128
1129 # TODO: this might not apply now that we have BaseUserMapping - test
1130324µs if ( $cUID eq $Foswiki::cfg{SuperAdminGroup} ) {
1131 $isAdmin = 1;
1132 }
1133 else {
1134 my $sag = $Foswiki::cfg{SuperAdminGroup};
1135183.7ms $isAdmin = $this->isInGroup( $cUID, $sag );
# spent 83.7ms making 1 call to Foswiki::UserMapping::isInGroup
1136 }
1137
1138 return $isAdmin;
1139}
1140
1141=begin TML
1142
1143---++ ObjectMethod findUserByEmail( $email ) -> \@cUIDs
1144 * =$email= - email address to look up
1145Return a list of canonical user names for the users that have this email
1146registered with the password manager or the user mapping manager.
1147
1148The password manager is asked first for whether it maps emails.
1149If it doesn't, then the user mapping manager is asked instead.
1150
1151=cut
1152
1153sub findUserByEmail {
1154 my ( $this, $email ) = @_;
1155 ASSERT($email) if DEBUG;
1156 my @users;
1157 if ( $this->{passwords}->isManagingEmails() ) {
1158 my $logins = $this->{passwords}->findUserByEmail($email);
1159 if ( defined $logins ) {
1160 foreach my $l (@$logins) {
1161 $l = $this->login2cUID($l);
1162 push( @users, $l ) if $l;
1163 }
1164 }
1165 }
1166 else {
1167
1168 # if the password manager didn't want to provide the service, ask
1169 # the user mapping manager
1170 unless ( $this->{_MAP_OF_EMAILS} ) {
1171 $this->{_MAP_OF_EMAILS} = {};
1172 my $it = $this->eachUser();
1173 while ( $it->hasNext() ) {
1174 my $uo = $it->next();
1175 map { push( @{ $this->{_MAP_OF_EMAILS}->{$_} }, $uo ); }
1176 $this->getEmails($uo);
1177 }
1178 }
1179 push( @users, @{ $this->{_MAP_OF_EMAILS}->{$email} } );
1180 }
1181 return \@users;
1182}
1183
1184=begin TML
1185
1186---++ ObjectMethod getEmails($name) -> @emailAddress
1187
1188If $name is a user, return their email addresses. If it is a group,
1189return the addresses of everyone in the group.
1190
1191The password manager and user mapping manager are both consulted for emails
1192for each user (where they are actually found is implementation defined).
1193
1194Duplicates are removed from the list.
1195
1196=cut
1197
1198sub getEmails {
1199 my ( $this, $user, $seen ) = @_;
1200
1201 $seen ||= {};
1202
1203 my %emails = ();
1204
1205 if ( $seen->{$user} ) {
1206
1207 #print STDERR "preventing infinit recursion in getEmails($user)\n";
1208 }
1209 else {
1210 $seen->{$user} = 1;
1211
1212 if ( $this->isGroup($user) ) {
1213 my $it = $this->eachGroupMember($user);
1214 while ( $it->hasNext() ) {
1215 foreach ( $this->getEmails( $it->next(), $seen ) ) {
1216 $emails{$_} = 1;
1217 }
1218 }
1219 }
1220 else {
1221 if ( $this->{passwords}->isManagingEmails() ) {
1222
1223 # get emails from the password manager
1224 foreach ( $this->{passwords}
1225 ->getEmails( $this->getLoginName($user), $seen ) )
1226 {
1227 $emails{$_} = 1;
1228 }
1229 }
1230 else {
1231
1232 # And any on offer from the user mapping manager
1233 foreach ( mapper_getEmails( $this->{session}, $user ) ) {
1234 $emails{$_} = 1;
1235 }
1236 }
1237 }
1238 }
1239 return keys %emails;
1240}
1241
1242=begin TML
1243
1244---++ ObjectMethod setEmails($cUID, @emails) -> boolean
1245
1246Set the email address(es) for the given user.
1247The password manager is tried first, and if it doesn't want to know the
1248user mapping manager is tried.
1249
1250=cut
1251
1252sub setEmails {
1253 my $this = shift;
1254 my $user = shift;
1255
1256 if ( $this->{passwords}->isManagingEmails() ) {
1257 $this->{passwords}->setEmails( $this->getLoginName($user), @_ );
1258 }
1259 else {
1260 mapper_setEmails( $this->{session}, $user, @_ );
1261 }
1262}
1263
1264=begin TML
1265
1266---++ StaticMethod mapper_getEmails($session, $user)
1267
1268Only used if passwordManager->isManagingEmails= = =false
1269(The emails are stored in the user topics.
1270
1271Note: This method is PUBLIC because it is used by the tools/upgrade_emails.pl
1272script, which needs to kick down to the mapper to retrieve email addresses
1273from Wiki topics.
1274
1275=cut
1276
1277sub mapper_getEmails {
1278 my ( $session, $user ) = @_;
1279
1280 my $topicObject = Foswiki::Meta->load(
1281 $session,
1282 $Foswiki::cfg{UsersWebName},
1283 $session->{users}->getWikiName($user)
1284 );
1285
1286 my @addresses;
1287
1288 # Try the form first
1289 my $entry = $topicObject->get( 'FIELD', 'Email' );
1290 if ($entry) {
1291 push( @addresses, split( /;/, $entry->{value} ) );
1292 }
1293 else {
1294
1295 # Now try the topic text
1296 foreach my $l ( split( /\r?\n/, $topicObject->text ) ) {
1297 if ( $l =~ /^\s+\*\s+E-?mail:\s*(.*)$/mi ) {
1298
1299 # SMELL: implicit unvalidated untaint
1300 push @addresses, split( /;/, $1 );
1301 }
1302 }
1303 }
1304
1305 return @addresses;
1306}
1307
1308=begin TML
1309
1310---++ StaticMethod mapper_setEmails ($session, $user, @emails)
1311
1312Only used if =passwordManager->isManagingEmails= = =false=.
1313(emails are stored in user topics
1314
1315=cut
1316
1317sub mapper_setEmails {
1318 my $session = shift;
1319 my $cUID = shift;
1320
1321 my $mails = join( ';', @_ );
1322
1323 my $user = $session->{users}->getWikiName($cUID);
1324
1325 my $topicObject =
1326 Foswiki::Meta->load( $session, $Foswiki::cfg{UsersWebName}, $user );
1327
1328 if ( $topicObject->get('FORM') ) {
1329
1330 # use the form if there is one
1331 $topicObject->putKeyed(
1332 'FIELD',
1333 {
1334 name => 'Email',
1335 value => $mails,
1336 title => 'Email',
1337 attributes => 'h'
1338 }
1339 );
1340 }
1341 else {
1342
1343 # otherwise use the topic text
1344 my $text = $topicObject->text() || '';
1345 unless ( $text =~ s/^(\s+\*\s+E-?mail:\s*).*$/$1$mails/mi ) {
1346 $text .= "\n * Email: $mails\n";
1347 }
1348 $topicObject->text($text);
1349 }
1350
1351 $topicObject->save();
1352}
1353
1354=begin TML
1355
1356---++ ObjectMethod findUserByWikiName ($wikiname) -> list of cUIDs associated with that wikiname
1357
1358See baseclass for documentation
1359
1360The $skipExistanceCheck parameter
1361is private to this module, and blocks the standard existence check
1362to avoid reading .htpasswd when checking group memberships).
1363
1364=cut
1365
1366
# spent 2.12ms (141µs+1.97) within Foswiki::Users::TopicUserMapping::findUserByWikiName which was called 3 times, avg 705µs/call: # 3 times (141µs+1.97ms) by Foswiki::Users::findUserByWikiName at line 517 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Users.pm, avg 705µs/call
sub findUserByWikiName {
13671262µs my ( $this, $wn, $skipExistanceCheck ) = @_;
1368 my @users = ();
1369
1370652µs337µs if ( $this->isGroup($wn) ) {
# spent 37µs making 3 calls to Foswiki::Users::TopicUserMapping::isGroup, avg 12µs/call
1371 push( @users, $wn );
1372 }
1373 elsif ( $Foswiki::cfg{Register}{AllowLoginName} ) {
1374
1375 # print STDERR "AllowLoginName discovered \n";
1376
1377 # Add additional mappings defined in WikiUsers
1378 $this->_loadMapping();
1379 if ( $this->{W2U}->{$wn} ) {
1380
1381 # Wikiname to UID mapping is defined
1382 my $user = $this->{W2U}->{$wn};
1383 push( @users, $user ) if $user;
1384 }
1385 else {
1386
1387 # Bloody compatibility!
1388 # The wikiname is always a registered user for the purposes of this
1389 # mapping. We have to do this because Foswiki defines access controls
1390 # in terms of mapped users, and if a wikiname is *missing* from the
1391 # mapping there is "no such user".
1392 my $user = $this->login2cUID($wn);
1393 push( @users, $user ) if $user;
1394 }
1395 }
1396 else {
1397
1398 # print STDERR "NOT AllowLoginName \n";
1399
1400 # The wikiname is also the login name, so we can just convert
1401 # it directly to a cUID
14023708µs my $cUID = $this->login2cUID($wn);
# spent 708µs making 3 calls to Foswiki::Users::TopicUserMapping::login2cUID, avg 236µs/call
1403
1404 # print STDERR "login2cUID for $wn returned $cUID \n";
1405
1406 # print STDERR "$wn EXISTS \n" if ( $cUID && $this->userExists($cUID) );
140739µs31.23ms if ( $skipExistanceCheck || ( $cUID && $this->userExists($cUID) ) ) {
# spent 1.23ms making 3 calls to Foswiki::Users::TopicUserMapping::userExists, avg 409µs/call
1408 push( @users, $cUID );
1409 }
1410 }
1411 return \@users;
1412}
1413
1414=begin TML
1415
1416---++ ObjectMethod checkPassword( $login, $password ) -> $boolean
1417
1418Finds if the password is valid for the given user.
1419
1420Returns 1 on success, undef on failure.
1421
1422=cut
1423
1424sub checkPassword {
1425 my ( $this, $login, $pw ) = @_;
1426
1427 # If we don't have a PasswordManager and use TemplateLogin, always allow login
1428 return 1
1429 if ( $Foswiki::cfg{PasswordManager} eq 'none'
1430 && $Foswiki::cfg{LoginManager} eq
1431 'Foswiki::LoginManager::TemplateLogin' );
1432
1433 return $this->{passwords}->checkPassword( $login, $pw );
1434}
1435
1436=begin TML
1437
1438---++ ObjectMethod setPassword( $cUID, $newPassU, $oldPassU ) -> $boolean
1439
1440BEWARE: $user should be a cUID, but is a login when the resetPassword
1441functionality is used.
1442The UserMapper needs to convert either one to a valid login for use by
1443the Password manager
1444
1445TODO: needs fixing
1446
1447If the $oldPassU matches matches the user's password, then it will
1448replace it with $newPassU.
1449
1450If $oldPassU is not correct and not 1, will return 0.
1451
1452If $oldPassU is 1, will force the change irrespective of
1453the existing password, adding the user if necessary.
1454
1455Otherwise returns 1 on success, undef on failure.
1456
1457=cut
1458
1459sub setPassword {
1460 my ( $this, $user, $newPassU, $oldPassU ) = @_;
1461 ASSERT($user) if DEBUG;
1462 my $login = $this->getLoginName($user) || $user;
1463 return $this->{passwords}->setPassword( $login, $newPassU, $oldPassU );
1464}
1465
1466=begin TML
1467
1468---++ ObjectMethod passwordError( ) -> $string
1469
1470returns a string indicating the error that happened in the password handlers
1471TODO: these delayed error's should be replaced with Exceptions.
1472
1473returns undef if no error
1474
1475=cut
1476
1477sub passwordError {
1478 my ($this) = @_;
1479 return $this->{passwords}->error();
1480}
1481
1482# TODO: and probably flawed in light of multiple cUIDs mapping to one wikiname
1483
# spent 36.2ms (15.9+20.3) within Foswiki::Users::TopicUserMapping::_cacheUser which was called 401 times, avg 90µs/call: # 401 times (15.9ms+20.3ms) by Foswiki::Users::TopicUserMapping::_loadMapping at line 1600, avg 90µs/call
sub _cacheUser {
1484440414.2ms my ( $this, $wikiname, $login ) = @_;
14854011.31ms ASSERT($wikiname) if DEBUG;
# spent 1.31ms making 401 calls to Assert::ASSERTS_OFF, avg 3µs/call
1486
1487 $login ||= $wikiname;
1488
1489 #discard users that are the BaseUserMapper's responsibility
1490 return
14914014.21ms if ( $this->{session}->{users}->{basemapping}
# spent 4.21ms making 401 calls to Foswiki::Users::BaseUserMapping::handlesUser, avg 11µs/call
1492 ->handlesUser( undef, $login, $wikiname ) );
1493
149440013.4ms my $cUID = $this->login2cUID( $login, 1 );
# spent 13.4ms making 400 calls to Foswiki::Users::TopicUserMapping::login2cUID, avg 33µs/call
1495 return unless ($cUID);
14964001.38ms ASSERT($cUID) if DEBUG;
# spent 1.38ms making 400 calls to Assert::ASSERTS_OFF, avg 3µs/call
1497
1498 #$this->{U2L}->{$cUID} = $login;
1499 $this->{U2W}->{$cUID} = $wikiname;
1500 $this->{L2U}->{$login} = $cUID;
1501 $this->{W2U}->{$wikiname} = $cUID;
1502
1503 return $cUID;
1504}
1505
1506# callback for search function to collate results
1507
# spent 462µs within Foswiki::Users::TopicUserMapping::_collateGroups which was called 44 times, avg 11µs/call: # 44 times (462µs+0s) by Foswiki::Search::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Search.pm:1327] at line 1326 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Search.pm, avg 11µs/call
sub _collateGroups {
1508154468µs my $ref = shift;
1509 my $group = shift;
1510 return unless $group;
15112255µs push( @{ $ref->{list} }, $group );
1512}
1513
1514# get a list of groups defined in this Wiki
1515
# spent 3.36s (137µs+3.36) within Foswiki::Users::TopicUserMapping::_getListOfGroups which was called 2 times, avg 1.68s/call: # 2 times (137µs+3.36s) by Foswiki::Users::TopicUserMapping::eachGroup at line 718, avg 1.68s/call
sub _getListOfGroups {
15161050µs my $this = shift;
1517 my $reset = shift;
1518
151927µs ASSERT( $this->isa('Foswiki::Users::TopicUserMapping') ) if DEBUG;
# spent 7µs making 2 calls to Assert::ASSERTS_OFF, avg 3µs/call
1520
1521573µs if ( !$this->{groupsList} || $reset ) {
1522 my $users = $this->{session}->{users};
1523 $this->{groupsList} = [];
1524
1525 #create a MetaCache _before_ we do silly things with the session's users
1526229µs $this->{session}->search->metacache();
# spent 15µs making 1 call to Foswiki::Search::metacache # spent 14µs making 1 call to Foswiki::search
1527
1528 # Temporarily set the user to admin, otherwise it cannot see groups
1529 # where %USERSWEB% is protected from view
1530 local $this->{session}->{user} = $Foswiki::cfg{SuperAdminGroup};
1531
153223.36s $this->{session}->search->searchWeb(
# spent 3.36s making 1 call to Foswiki::Search::searchWeb # spent 7µs making 1 call to Foswiki::search
1533 _callback => \&_collateGroups,
1534 _cbdata => {
1535 list => $this->{groupsList},
1536 users => $users
1537 },
1538 web => $Foswiki::cfg{UsersWebName},
1539 topic => "*Group",
1540 scope => 'topic',
1541 search => '1',
1542 type => 'query',
1543 nosummary => 'on',
1544 nosearch => 'on',
1545 noheader => 'on',
1546 nototal => 'on',
1547 noempty => 'on',
1548 format => '$topic',
1549 separator => '',
1550 );
1551 }
1552 return $this->{groupsList};
1553}
1554
1555# Build hash to translate between username (e.g. jsmith)
1556# and WikiName (e.g. Main.JaneSmith).
1557# PRIVATE subclasses should *not* implement this.
1558
# spent 62.8ms (8.12+54.7) within Foswiki::Users::TopicUserMapping::_loadMapping which was called 63 times, avg 996µs/call: # 63 times (8.12ms+54.7ms) by Foswiki::Users::TopicUserMapping::handlesUser at line 161, avg 996µs/call
sub _loadMapping {
1559128472µs my $this = shift;
1560
1561 return if $this->{CACHED};
1562 $this->{CACHED} = 1;
1563
1564 #TODO: should only really do this mapping IF the user is in the password file.
1565 # except if we can't 'fetchUsers' like in the Passord='none' case -
1566 # in which case the only time we
1567 # know a login is real, is when they are logged in :(
1568219µs14µs if ( ( $Foswiki::cfg{Register}{AllowLoginName} )
# spent 4µs making 1 call to Foswiki::Users::HtPasswdUser::canFetchUsers
1569 || ( !$this->{passwords}->canFetchUsers() ) )
1570 {
1571 my $session = $this->{session};
1572 if (
1573 $session->topicExists(
1574 $Foswiki::cfg{UsersWebName},
1575 $Foswiki::cfg{UsersTopicName}
1576 )
1577 )
1578 {
1579 my $usersTopicObject = Foswiki::Meta->load(
1580 $session,
1581 $Foswiki::cfg{UsersWebName},
1582 $Foswiki::cfg{UsersTopicName}
1583 );
1584 my $text = $usersTopicObject->text() || '';
1585
1586 # Get the WikiNames and userids, and build hashes in both directions
1587 # This matches:
1588 # * WikiGuest - guest - 10 Mar 2005
1589 # * WikiGuest - 10 Mar 2005
1590 $text =~
1591s/^\s*\* (?:$Foswiki::regex{webNameRegex}\.)?($Foswiki::regex{wikiWordRegex})\s*(?:-\s*(\S+)\s*)?-.*$/(_cacheUser( $this, $1, $2)||'')/gome;
1592 }
1593 }
1594 else {
1595
1596 #loginnames _are_ WikiNames so ask the Password handler for list of users
15971662µs my $iter = $this->{passwords}->fetchUsers();
# spent 662µs making 1 call to Foswiki::Users::HtPasswdUser::fetchUsers
1598132µs while ( $iter->hasNext() ) {
# spent 32µs making 1 call to Foswiki::ListIterator::hasNext
15998026.28ms4018.42ms my $login = $iter->next();
# spent 8.42ms making 401 calls to Foswiki::ListIterator::next, avg 21µs/call
160080245.5ms _cacheUser( $this, $login, $login );
# spent 36.2ms making 401 calls to Foswiki::Users::TopicUserMapping::_cacheUser, avg 90µs/call # spent 9.30ms making 401 calls to Foswiki::ListIterator::hasNext, avg 23µs/call
1601 }
1602 }
1603}
1604
1605# Get a list of *canonical user ids* from a text string containing a
1606# list of user *wiki* names, *login* names, and *group ids*.
1607
# spent 84.2ms (1.63+82.6) within Foswiki::Users::TopicUserMapping::_expandUserList which was called 2 times, avg 42.1ms/call: # 2 times (1.63ms+82.6ms) by Foswiki::Users::TopicUserMapping::eachGroupMember at line 665, avg 42.1ms/call
sub _expandUserList {
16081492µs my ( $this, $names, $expand ) = @_;
1609
1610 $expand = 1 unless ( defined $expand );
1611
1612 # print STDERR "_expandUserList called $names - expand $expand \n";
1613
1614 $names ||= '';
1615
1616 # comma delimited list of users or groups
1617 # i.e.: "%MAINWEB%.UserA, UserB, Main.UserC # something else"
161829µs $names =~ s/(<[^>]*>)//go; # Remove HTML tags
# spent 9µs making 2 calls to Foswiki::Users::TopicUserMapping::CORE:subst, avg 5µs/call
1619
1620 my @l;
1621 foreach my $ident ( split( /[\,\s]+/, $names ) ) {
1622
1623 # Dump the web specifier if userweb
162499773µs66155µs $ident =~ s/^($Foswiki::cfg{UsersWebName}|%USERSWEB%|%MAINWEB%)\.//;
# spent 96µs making 33 calls to Foswiki::Users::TopicUserMapping::CORE:regcomp, avg 3µs/call # spent 59µs making 33 calls to Foswiki::Users::TopicUserMapping::CORE:subst, avg 2µs/call
1625 next unless $ident;
162699365µs33447µs if ( $this->isGroup($ident) ) {
# spent 447µs making 33 calls to Foswiki::Users::TopicUserMapping::isGroup, avg 14µs/call
1627 if ( !$expand ) {
1628 push( @l, $ident );
1629 }
1630 else {
1631 my $it =
1632 $this->eachGroupMember( $ident, { expand => $expand } );
1633 while ( $it->hasNext() ) {
1634 push( @l, $it->next() );
1635 }
1636 }
1637 }
1638 else {
1639
1640 # Might be a wiki name (wiki names may map to several cUIDs)
1641 my %namelist =
16423369.0ms map { $_ => 1 }
# spent 69.0ms making 33 calls to Foswiki::Users::findUserByWikiName, avg 2.09ms/call
164336210µs @{ $this->{session}->{users}->findUserByWikiName($ident) };
1644
1645 # If we were not successful in finding by WikiName we assumed it
1646 # may be a login name (login names map to a single cUID).
1647 # If user is unknown we return whatever was listed so we can
1648 # remove deleted or misspelled users
164960269µs unless (%namelist) {
16503013.0ms my $cUID = $this->{session}->{users}->getCanonicalUserID($ident)
# spent 13.0ms making 30 calls to Foswiki::Users::getCanonicalUserID, avg 433µs/call
1651 || $ident;
1652 $namelist{$cUID} = 1 if $cUID;
1653 }
1654 push( @l, keys %namelist );
1655 }
1656 }
1657 return \@l;
1658}
1659
166017µs1;
1661__END__
 
# spent 23.3ms within Foswiki::Users::TopicUserMapping::CORE:match which was called 12226 times, avg 2µs/call: # 12226 times (23.3ms+0s) by Foswiki::Users::TopicUserMapping::isGroup at line 692, avg 2µs/call
sub Foswiki::Users::TopicUserMapping::CORE:match; # opcode
# spent 120µs within Foswiki::Users::TopicUserMapping::CORE:regcomp which was called 35 times, avg 3µs/call: # 33 times (96µs+0s) by Foswiki::Users::TopicUserMapping::_expandUserList at line 1624, avg 3µs/call # 2 times (24µs+0s) by Foswiki::Users::TopicUserMapping::getWikiName at line 507, avg 12µs/call
sub Foswiki::Users::TopicUserMapping::CORE:regcomp; # opcode
# spent 124µs within Foswiki::Users::TopicUserMapping::CORE:subst which was called 56 times, avg 2µs/call: # 33 times (59µs+0s) by Foswiki::Users::TopicUserMapping::_expandUserList at line 1624, avg 2µs/call # 19 times (50µs+0s) by Foswiki::Users::TopicUserMapping::getLoginName at line 217, avg 3µs/call # 2 times (9µs+0s) by Foswiki::Users::TopicUserMapping::_expandUserList at line 1618, avg 5µs/call # 2 times (6µs+0s) by Foswiki::Users::TopicUserMapping::getWikiName at line 507, avg 3µs/call
sub Foswiki::Users::TopicUserMapping::CORE:subst; # opcode