Filename | /usr/local/src/github.com/foswiki/core/lib/Foswiki/Users/TopicUserMapping.pm |
Statements | Executed 45320 statements in 222ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
12226 | 5 | 3 | 143ms | 3.53s | isGroup | Foswiki::Users::TopicUserMapping::
12226 | 1 | 1 | 23.3ms | 23.3ms | CORE:match (opcode) | Foswiki::Users::TopicUserMapping::
401 | 1 | 1 | 15.9ms | 36.2ms | _cacheUser | Foswiki::Users::TopicUserMapping::
1 | 1 | 1 | 14.8ms | 15.3ms | BEGIN@36 | Foswiki::Users::TopicUserMapping::
63 | 1 | 1 | 8.12ms | 62.8ms | _loadMapping | Foswiki::Users::TopicUserMapping::
1 | 1 | 1 | 6.07ms | 18.8ms | new | Foswiki::Users::TopicUserMapping::
410 | 4 | 2 | 5.09ms | 15.5ms | login2cUID | Foswiki::Users::TopicUserMapping::
114 | 1 | 1 | 2.18ms | 89.6ms | handlesUser | Foswiki::Users::TopicUserMapping::
2 | 1 | 1 | 1.63ms | 84.2ms | _expandUserList | Foswiki::Users::TopicUserMapping::
61 | 3 | 1 | 1.48ms | 29.2ms | _userReallyExists | Foswiki::Users::TopicUserMapping::
1 | 1 | 1 | 1.19ms | 1.32ms | BEGIN@35 | Foswiki::Users::TopicUserMapping::
1 | 1 | 1 | 1.17ms | 1.24ms | finish | Foswiki::Users::TopicUserMapping::
16 | 1 | 1 | 597µs | 4.20ms | getWikiName | Foswiki::Users::TopicUserMapping::
19 | 3 | 2 | 575µs | 4.75ms | getLoginName | Foswiki::Users::TopicUserMapping::
44 | 1 | 1 | 462µs | 462µs | _collateGroups | Foswiki::Users::TopicUserMapping::
3 | 2 | 2 | 291µs | 91.4ms | eachGroupMember | Foswiki::Users::TopicUserMapping::
3 | 1 | 1 | 141µs | 2.12ms | findUserByWikiName | Foswiki::Users::TopicUserMapping::
2 | 1 | 1 | 137µs | 3.36s | _getListOfGroups | Foswiki::Users::TopicUserMapping::
56 | 4 | 1 | 124µs | 124µs | CORE:subst (opcode) | Foswiki::Users::TopicUserMapping::
35 | 2 | 1 | 120µs | 120µs | CORE:regcomp (opcode) | Foswiki::Users::TopicUserMapping::
3 | 1 | 1 | 119µs | 1.23ms | userExists | Foswiki::Users::TopicUserMapping::
2 | 1 | 1 | 77µs | 83.8ms | isAdmin | Foswiki::Users::TopicUserMapping::
2 | 1 | 1 | 77µs | 3.36s | eachGroup | Foswiki::Users::TopicUserMapping::
1 | 1 | 1 | 26µs | 35µs | BEGIN@213 | Foswiki::Users::TopicUserMapping::
1 | 1 | 1 | 22µs | 61µs | BEGIN@33 | Foswiki::Users::TopicUserMapping::
1 | 1 | 1 | 22µs | 22µs | BEGIN@28 | Foswiki::Users::TopicUserMapping::
1 | 1 | 1 | 18µs | 25µs | BEGIN@218 | Foswiki::Users::TopicUserMapping::
1 | 1 | 1 | 18µs | 26µs | BEGIN@31 | Foswiki::Users::TopicUserMapping::
1 | 1 | 1 | 18µs | 37µs | BEGIN@32 | Foswiki::Users::TopicUserMapping::
1 | 1 | 1 | 16µs | 415µs | BEGIN@34 | Foswiki::Users::TopicUserMapping::
1 | 1 | 1 | 4µs | 4µs | supportsRegistration | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | __ANON__[:439] | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | __ANON__[:447] | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | __ANON__[:586] | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | __ANON__[:737] | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | _clearGroupCache | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | _writeGroupTopic | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | addUser | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | addUserToGroup | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | checkPassword | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | eachMembership | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | eachUser | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | findUserByEmail | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | getEmails | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | groupAllowsChange | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | groupAllowsView | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | mapper_getEmails | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | mapper_setEmails | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | passwordError | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | removeUser | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | removeUserFromGroup | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | setEmails | Foswiki::Users::TopicUserMapping::
0 | 0 | 0 | 0s | 0s | setPassword | Foswiki::Users::TopicUserMapping::
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 | |||||
7 | use | ||||
8 | |||||
9 | The User mapping is the process by which Foswiki maps from a username (a login name) | ||||
10 | to a wikiname and back. It is also where groups are defined. | ||||
11 | |||||
12 | By default Foswiki maintains user topics and group topics in the %MAINWEB% that | ||||
13 | define 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 | |||||
18 | Many sites will want to override this behaviour, for example to get users and groups from a corporate database. | ||||
19 | |||||
20 | This class implements the basic Foswiki behaviour using topics to store users, | ||||
21 | but is also designed to be subclassed so that other services can be used. | ||||
22 | |||||
23 | Subclasses should be named 'XxxxUserMapping' so that configure can find them. | ||||
24 | |||||
25 | =cut | ||||
26 | |||||
27 | package Foswiki::Users::TopicUserMapping; | ||||
28 | 2 | 71µs | 1 | 22µs | # spent 22µs within Foswiki::Users::TopicUserMapping::BEGIN@28 which was called:
# once (22µs+0s) by Foswiki::Users::new at line 28 # spent 22µs making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@28 |
29 | 1 | 14µs | our @ISA = ('Foswiki::UserMapping'); | ||
30 | |||||
31 | 2 | 45µs | 2 | 34µ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 # spent 26µs making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@31
# spent 8µs making 1 call to strict::import |
32 | 2 | 44µs | 2 | 56µ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 # spent 37µs making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@32
# spent 19µs making 1 call to warnings::import |
33 | 2 | 48µs | 2 | 101µ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 # spent 61µs making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@33
# spent 40µs making 1 call to Assert::import |
34 | 2 | 48µs | 2 | 813µ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 # spent 415µs making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@34
# spent 398µs making 1 call to Error::import |
35 | 2 | 161µs | 1 | 1.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 # spent 1.32ms making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@35 |
36 | 2 | 971µs | 1 | 15.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 # 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 | |||||
45 | Constructs a new user mapping handler of this type, referring to $session | ||||
46 | for 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. | ||||
52 | 1 | 2µs | our $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 | ||||
57 | 14 | 113µs | my ( $class, $session ) = @_; | ||
58 | |||||
59 | 1 | 23µ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 $@; | ||||
66 | 1 | 9.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. | ||||
71 | 1 | 100µ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() | ||||
96 | Break 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 | ||||
104 | 9 | 1.16ms | my $this = shift; | ||
105 | |||||
106 | 1 | 63µ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}; | ||||
113 | 1 | 11µs | $this->SUPER::finish(); # spent 11µs making 1 call to Foswiki::UserMapping::finish | ||
114 | } | ||||
115 | |||||
116 | =begin TML | ||||
117 | |||||
118 | ---++ ObjectMethod supportsRegistration () -> false | ||||
119 | return 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 | ||||
124 | 1 | 8µs | return 1; | ||
125 | } | ||||
126 | |||||
127 | =begin TML | ||||
128 | |||||
129 | ---++ ObjectMethod handlesUser ( $cUID, $login, $wikiname) -> $boolean | ||||
130 | |||||
131 | Called by the Foswiki::Users object to determine which loaded mapping | ||||
132 | to use for a given user. | ||||
133 | |||||
134 | The user can be identified by any of $cUID, $login or $wikiname. Any of | ||||
135 | these parameters may be undef, and they should be tested in order; cUID | ||||
136 | first, then login, then wikiname. This mapping is special - for backwards | ||||
137 | compatibility, it assumes responsibility for _all_ non BaseMapping users. | ||||
138 | If you're needing to mix the TopicUserMapping with other mappings, | ||||
139 | define $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 | ||||
144 | 506 | 1.41ms | my ( $this, $cUID, $login, $wikiname ) = @_; | ||
145 | 114 | 248µ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 | ||||
157 | 32 | 24.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 | ||||
160 | 126 | 458µs | if ($wikiname) { | ||
161 | 63 | 62.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 | |||||
172 | Convert a login name to the corresponding canonical user name. The | ||||
173 | canonical name can be any string of 7-bit alphanumeric and underscore | ||||
174 | characters, 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. | ||||
178 | This 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 | ||||
183 | 1230 | 5.03ms | my ( $this, $login, $dontcheck ) = @_; | ||
184 | |||||
185 | 10 | 50µs | unless ($dontcheck) { | ||
186 | 10 | 1.61ms | return unless ( _userReallyExists( $this, $login ) ); # spent 1.61ms making 10 calls to Foswiki::Users::TopicUserMapping::_userReallyExists, avg 161µs/call | ||
187 | } | ||||
188 | |||||
189 | 410 | 8.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 | |||||
196 | Converts 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 | ||||
202 | 124 | 577µs | my ( $this, $cUID ) = @_; | ||
203 | 19 | 60µ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 | |||||
213 | 2 | 91µs | 2 | 43µ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 # 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 | ||||
217 | 19 | 50µ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 | ||
218 | 2 | 8.64ms | 2 | 31µ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 # spent 25µs making 1 call to Foswiki::Users::TopicUserMapping::BEGIN@218
# spent 6µs making 1 call to bytes::unimport |
219 | |||||
220 | 19 | 2.98ms | return unless _userReallyExists( $this, $login ); # spent 2.98ms making 19 calls to Foswiki::Users::TopicUserMapping::_userReallyExists, avg 157µs/call | ||
221 | 5 | 952µ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 | ||||
224 | 5 | 124µ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 | ||||
230 | 183 | 575µ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 | |||||
241 | 156 | 764µs | 61 | 229µ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 | ||||
246 | 61 | 27.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 | |||||
263 | throws an Error::Simple | ||||
264 | |||||
265 | Add a user to the persistant mapping that maps from usernames to wikinames | ||||
266 | and 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). | ||||
269 | Names must be acceptable to $Foswiki::cfg{NameFilter} | ||||
270 | $login must *always* be specified. $wikiname may be undef, in which case | ||||
271 | the user mapper should make one up. | ||||
272 | This function must return a *canonical user id* that it uses to uniquely | ||||
273 | identify the user. This can be the login name, or the wikiname if they | ||||
274 | are all guaranteed unigue, or some other string consisting only of 7-bit | ||||
275 | alphanumerics and underscores. | ||||
276 | if 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 | |||||
282 | sub 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 | |||||
460 | Delete the users entry. Removes the user from the password | ||||
461 | manager and user mapping manager. Does *not* remove their personal | ||||
462 | topics, which may still be linked. | ||||
463 | |||||
464 | =cut | ||||
465 | |||||
466 | sub 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 | |||||
479 | Map a canonical user name to a wikiname. If it fails to find a | ||||
480 | WikiName, it will attempt to find a matching loginname, and use | ||||
481 | an escaped version of that. | ||||
482 | If 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 | ||||
487 | 112 | 371µs | my ( $this, $cUID ) = @_; | ||
488 | 16 | 53µs | ASSERT($cUID) if DEBUG; # spent 53µs making 16 calls to Assert::ASSERTS_OFF, avg 3µs/call | ||
489 | 16 | 50µ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 | |||||
502 | 32 | 131µs | unless ($wikiname) { | ||
503 | 16 | 3.47ms | $wikiname = $this->getLoginName($cUID); # spent 3.47ms making 16 calls to Foswiki::Users::TopicUserMapping::getLoginName, avg 217µs/call | ||
504 | 2 | 54µs | if ($wikiname) { | ||
505 | |||||
506 | # sanitise the generated WikiName | ||||
507 | 4 | 30µ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 | |||||
518 | Determine if the user already exists or not. Whether a user exists | ||||
519 | or 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 | ||||
524 | 19 | 99µs | my ( $this, $cUID ) = @_; | ||
525 | 3 | 10µ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 | |||||
530 | 2 | 831µ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 | ||||
536 | 2 | 25µ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). | ||||
539 | 4 | 243µ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 | |||||
567 | See baseclass for documentation | ||||
568 | |||||
569 | =cut | ||||
570 | |||||
571 | sub 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 | |||||
594 | See baseclass for documentation | ||||
595 | |||||
596 | =cut | ||||
597 | |||||
598 | 1 | 1µs | my %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 | ||||
601 | 32 | 181µs | my ( $this, $group, $options ) = @_; | ||
602 | |||||
603 | my $expand = $options->{expand}; | ||||
604 | |||||
605 | 3 | 10µ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 | |||||
621 | 1 | 13µs | if ( $expand && defined( $this->{eachGroupMember}->{$group} ) ) { | ||
622 | |||||
623 | # print STDERR "Returning cached expanded list for $group\n"; | ||||
624 | 1 | 22µ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 | ||||
636 | 2 | 5µ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 | |||||
644 | 8 | 43µs | 2 | 357µ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"; | ||||
650 | 2 | 4.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 | |||||
654 | 4 | 46µ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 { | ||||
665 | 4 | 85.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"; | ||||
675 | 2 | 52µ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 | |||||
682 | See 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 | ||||
687 | 36682 | 176ms | 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 | |||||
692 | 12226 | 23.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... | ||||
700 | 2 | 3.36s | my $iterator = $this->eachGroup(); # spent 3.36s making 2 calls to Foswiki::Users::TopicUserMapping::eachGroup, avg 1.68s/call | ||
701 | 2 | 60µs | while ( $iterator->hasNext() ) { # spent 60µs making 2 calls to Foswiki::ListIterator::hasNext, avg 30µs/call | ||
702 | 56 | 344µs | 28 | 640µs | my $groupname = $iterator->next(); # spent 640µs making 28 calls to Foswiki::ListIterator::next, avg 23µs/call |
703 | 26 | 578µ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 | |||||
712 | See 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 | ||||
717 | 6 | 79µs | my ($this) = @_; | ||
718 | 2 | 3.36s | _getListOfGroups($this); # spent 3.36s making 2 calls to Foswiki::Users::TopicUserMapping::_getListOfGroups, avg 1.68s/call | ||
719 | 2 | 4µs | 2 | 77µ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 | |||||
726 | See baseclass for documentation | ||||
727 | |||||
728 | =cut | ||||
729 | |||||
730 | sub 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 | |||||
745 | returns 1 if the group is able to be viewed by the current logged in user | ||||
746 | |||||
747 | implemented using topic VIEW permissions | ||||
748 | |||||
749 | =cut | ||||
750 | |||||
751 | sub 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 | |||||
778 | returns 1 if the group is able to be modified by $cuid | ||||
779 | |||||
780 | implemented using topic CHANGE permissions | ||||
781 | |||||
782 | =cut | ||||
783 | |||||
784 | sub 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 | ||||
811 | adds the user specified by the cuid to the group. | ||||
812 | If the group does not exist, it will return false and do nothing, unless the create flag is set. | ||||
813 | |||||
814 | cuid be a groupname which is added like it was an unknown user | ||||
815 | |||||
816 | =cut | ||||
817 | |||||
818 | sub 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. | ||||
929 | sub _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 | |||||
1018 | sub 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 | |||||
1089 | Removes the cache entries for unexpanded and expanded groups, | ||||
1090 | and searches un-expanded groups for any nesting group references | ||||
1091 | clearing them as well. | ||||
1092 | |||||
1093 | Note: This is not recursive and does not attempt to handle | ||||
1094 | more than one level of nested groups. | ||||
1095 | |||||
1096 | =cut | ||||
1097 | |||||
1098 | sub _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 | |||||
1119 | True 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 | ||||
1126 | 8 | 37µs | my ( $this, $cUID ) = @_; | ||
1127 | my $isAdmin = 0; | ||||
1128 | |||||
1129 | # TODO: this might not apply now that we have BaseUserMapping - test | ||||
1130 | 3 | 24µs | if ( $cUID eq $Foswiki::cfg{SuperAdminGroup} ) { | ||
1131 | $isAdmin = 1; | ||||
1132 | } | ||||
1133 | else { | ||||
1134 | my $sag = $Foswiki::cfg{SuperAdminGroup}; | ||||
1135 | 1 | 83.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 | ||||
1145 | Return a list of canonical user names for the users that have this email | ||||
1146 | registered with the password manager or the user mapping manager. | ||||
1147 | |||||
1148 | The password manager is asked first for whether it maps emails. | ||||
1149 | If it doesn't, then the user mapping manager is asked instead. | ||||
1150 | |||||
1151 | =cut | ||||
1152 | |||||
1153 | sub 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 | |||||
1188 | If $name is a user, return their email addresses. If it is a group, | ||||
1189 | return the addresses of everyone in the group. | ||||
1190 | |||||
1191 | The password manager and user mapping manager are both consulted for emails | ||||
1192 | for each user (where they are actually found is implementation defined). | ||||
1193 | |||||
1194 | Duplicates are removed from the list. | ||||
1195 | |||||
1196 | =cut | ||||
1197 | |||||
1198 | sub 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 | |||||
1246 | Set the email address(es) for the given user. | ||||
1247 | The password manager is tried first, and if it doesn't want to know the | ||||
1248 | user mapping manager is tried. | ||||
1249 | |||||
1250 | =cut | ||||
1251 | |||||
1252 | sub 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 | |||||
1268 | Only used if passwordManager->isManagingEmails= = =false | ||||
1269 | (The emails are stored in the user topics. | ||||
1270 | |||||
1271 | Note: This method is PUBLIC because it is used by the tools/upgrade_emails.pl | ||||
1272 | script, which needs to kick down to the mapper to retrieve email addresses | ||||
1273 | from Wiki topics. | ||||
1274 | |||||
1275 | =cut | ||||
1276 | |||||
1277 | sub 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 | |||||
1312 | Only used if =passwordManager->isManagingEmails= = =false=. | ||||
1313 | (emails are stored in user topics | ||||
1314 | |||||
1315 | =cut | ||||
1316 | |||||
1317 | sub 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 | |||||
1358 | See baseclass for documentation | ||||
1359 | |||||
1360 | The $skipExistanceCheck parameter | ||||
1361 | is private to this module, and blocks the standard existence check | ||||
1362 | to 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 | ||||
1367 | 12 | 62µs | my ( $this, $wn, $skipExistanceCheck ) = @_; | ||
1368 | my @users = (); | ||||
1369 | |||||
1370 | 6 | 52µs | 3 | 37µ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 | ||||
1402 | 3 | 708µ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) ); | ||||
1407 | 3 | 9µs | 3 | 1.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 | |||||
1418 | Finds if the password is valid for the given user. | ||||
1419 | |||||
1420 | Returns 1 on success, undef on failure. | ||||
1421 | |||||
1422 | =cut | ||||
1423 | |||||
1424 | sub 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 | |||||
1440 | BEWARE: $user should be a cUID, but is a login when the resetPassword | ||||
1441 | functionality is used. | ||||
1442 | The UserMapper needs to convert either one to a valid login for use by | ||||
1443 | the Password manager | ||||
1444 | |||||
1445 | TODO: needs fixing | ||||
1446 | |||||
1447 | If the $oldPassU matches matches the user's password, then it will | ||||
1448 | replace it with $newPassU. | ||||
1449 | |||||
1450 | If $oldPassU is not correct and not 1, will return 0. | ||||
1451 | |||||
1452 | If $oldPassU is 1, will force the change irrespective of | ||||
1453 | the existing password, adding the user if necessary. | ||||
1454 | |||||
1455 | Otherwise returns 1 on success, undef on failure. | ||||
1456 | |||||
1457 | =cut | ||||
1458 | |||||
1459 | sub 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 | |||||
1470 | returns a string indicating the error that happened in the password handlers | ||||
1471 | TODO: these delayed error's should be replaced with Exceptions. | ||||
1472 | |||||
1473 | returns undef if no error | ||||
1474 | |||||
1475 | =cut | ||||
1476 | |||||
1477 | sub 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 | ||||
1484 | 4404 | 14.2ms | my ( $this, $wikiname, $login ) = @_; | ||
1485 | 401 | 1.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 | ||||
1491 | 401 | 4.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 | |||||
1494 | 400 | 13.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); | ||||
1496 | 400 | 1.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 | ||||
1508 | 154 | 468µs | my $ref = shift; | ||
1509 | my $group = shift; | ||||
1510 | return unless $group; | ||||
1511 | 22 | 55µ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 | ||||
1516 | 10 | 50µs | my $this = shift; | ||
1517 | my $reset = shift; | ||||
1518 | |||||
1519 | 2 | 7µs | ASSERT( $this->isa('Foswiki::Users::TopicUserMapping') ) if DEBUG; # spent 7µs making 2 calls to Assert::ASSERTS_OFF, avg 3µs/call | ||
1520 | |||||
1521 | 5 | 73µ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 | ||||
1526 | 2 | 29µ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 | |||||
1532 | 2 | 3.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 | ||||
1559 | 128 | 472µ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 :( | ||||
1568 | 2 | 19µs | 1 | 4µ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 =~ | ||||
1591 | s/^\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 | ||||
1597 | 1 | 662µs | my $iter = $this->{passwords}->fetchUsers(); # spent 662µs making 1 call to Foswiki::Users::HtPasswdUser::fetchUsers | ||
1598 | 1 | 32µs | while ( $iter->hasNext() ) { # spent 32µs making 1 call to Foswiki::ListIterator::hasNext | ||
1599 | 802 | 6.28ms | 401 | 8.42ms | my $login = $iter->next(); # spent 8.42ms making 401 calls to Foswiki::ListIterator::next, avg 21µs/call |
1600 | 802 | 45.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 | ||||
1608 | 14 | 92µ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" | ||||
1618 | 2 | 9µ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 | ||||
1624 | 99 | 773µs | 66 | 155µ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; | ||||
1626 | 99 | 365µs | 33 | 447µ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 = | ||||
1642 | 33 | 69.0ms | map { $_ => 1 } # spent 69.0ms making 33 calls to Foswiki::Users::findUserByWikiName, avg 2.09ms/call | ||
1643 | 36 | 210µ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 | ||||
1649 | 60 | 269µs | unless (%namelist) { | ||
1650 | 30 | 13.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 | |||||
1660 | 1 | 7µs | 1; | ||
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 | |||||
# 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 | |||||
# 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 |