├── phpbb-external-auth-api
├── .gitignore
├── config.dist.php
├── COPYING
└── auth_api.php
├── crowd-phpbb-directory-server
├── src
│ └── main
│ │ ├── resources
│ │ └── atlassian-plugin.xml
│ │ └── java
│ │ └── com
│ │ └── phpbb
│ │ └── crowd
│ │ ├── EntityCreator.java
│ │ ├── GroupEntityCreator.java
│ │ ├── UserEntityCreator.java
│ │ └── phpBBDirectoryServer.java
├── pom.xml
└── LICENSE
└── README.markdown
/phpbb-external-auth-api/.gitignore:
--------------------------------------------------------------------------------
1 | config.php
2 |
--------------------------------------------------------------------------------
/crowd-phpbb-directory-server/src/main/resources/atlassian-plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | ${project.description}
4 | ${project.version}
5 |
6 |
7 | com.phpbb.crowd,*;resolution:=optional
8 |
9 |
10 |
--------------------------------------------------------------------------------
/phpbb-external-auth-api/config.dist.php:
--------------------------------------------------------------------------------
1 | false,
6 |
7 | // Debug calls? Writes to log file, set to false to disable or point to log file (absolute path)
8 | 'debug_api' => false,
9 |
10 | // Do not write to log file if output has more than 50 entries
11 | 'debug_api_limit_output' => 50,
12 |
13 | // enable user cache to create less burden on the database if crowd refreshes it's user list cache. Set to time in seconds.
14 | 'api_cache_users' => false,
15 |
16 | // Define last login period in days (only users fetched who where active within the last X days)
17 | 'last_login_period' => 2 * 365,
18 |
19 | // Exclude banned users from user list?
20 | 'exclude_banned_users' => true,
21 |
22 | // Custom profile field for first and last name? false to disable
23 | 'firstname_column' => 'first_name',
24 | 'lastname_column' => 'last_name',
25 |
26 | // Allowed IP's
27 | 'allowed_ips' => array(
28 | '127.0.0.1' => true,
29 | ),
30 |
31 | // Groups we want to not have in our directory
32 | // This is extremely useful, because this ensures a clean directory - Atlassian Tools try to index and check every single group, regardless of it's status/connection
33 | 'exclude_groups' => array(
34 | 228654, // Bots
35 | 228649, // Guests
36 | 228735, // Newly Registered Users
37 | 228651, // COPPA
38 | 228725, // on moderation queue
39 | 228695, // without edit
40 | 84421, // Former Team Members
41 | 228841, // Obvious spambots
42 | ),
43 | );
44 |
--------------------------------------------------------------------------------
/crowd-phpbb-directory-server/src/main/java/com/phpbb/crowd/EntityCreator.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 phpBB Ltd.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.phpbb.crowd;
17 |
18 | import com.atlassian.crowd.exception.*;
19 |
20 | public abstract class EntityCreator
21 | {
22 | private long directoryId;
23 |
24 | public EntityCreator(long directoryId)
25 | {
26 | setDirectoryId(directoryId);
27 | }
28 |
29 | public long getDirectoryId()
30 | {
31 | return directoryId;
32 | }
33 |
34 | public void setDirectoryId(long directoryId)
35 | {
36 | this.directoryId = directoryId;
37 | }
38 |
39 | public Object fromLine(String line)
40 | throws ObjectNotFoundException
41 | {
42 | String[] values = line.split("\t");
43 |
44 | if (values.length < this.minProperties())
45 | {
46 | throw new ObjectNotFoundException();
47 | }
48 |
49 | return this.hydrateObject(values);
50 | }
51 |
52 | abstract public int minProperties();
53 | abstract public Object hydrateObject(String[] values);
54 | }
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | # phpBB Directory Server for Atlassian Crowd
2 | The phpBB Directory Server is in `com.phpbb.crowd.phpBBDirectoryServer`. It implements `com.atlassian.crowd.integration.directory.RemoteDirectory` and can easily be configured as a Directory in Crowd. It does not support any write operations.
3 |
4 | ## Requirements
5 | * Crowd: 3.3.3+
6 | * phpBB: 3.3.0+
7 |
8 | ## Installing and Configuring the phpBB Directory Server
9 | Install the Atlassian SDK. Using its `atlas-package` command you can easily build the phpbbauth jar file from the `crowd-phpbb-auth` directory. Place this jar file into `crowd/webapp/WEB-INF/lib`. Make sure to export the `CROWD_PHPBB_ROOT_URL` environment variable, it needs to contain the URL to the root of your phpBB board. Install the phpBB External Authentication API. In Crowd there is a top level Directories tab. Here you can add a new directory. In the "Implementation Class" field you have to fill in "`com.phpbb.crowd.phpBBDirectoryServer`". You should disable all possible write operations in the Permissions tab - even if you don't, all write operations will throw Exceptions. The directory server also does not support any attributes. So even if they show up in the GUI they will not be saved by phpBB.
10 |
11 | # phpBB External Authentication API
12 | The API is rather simple for now. It consists of a single PHP file, to be placed in the phpBB root directory. Copy the values from `config.dist.php` into your board's `config.php`. The file replies to a number of POST requests. You can ask it to authenticate a user, password pair against the selected authentication method. You can query the user and group tables individually or join them to retrieve information about group memberships. The interface needs to be cleaned up after which it will make more sense to document all the functionality.
--------------------------------------------------------------------------------
/crowd-phpbb-directory-server/src/main/java/com/phpbb/crowd/GroupEntityCreator.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 phpBB Ltd.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.phpbb.crowd;
17 |
18 | import com.atlassian.crowd.exception.*;
19 | import com.atlassian.crowd.model.group.*;
20 |
21 | import com.phpbb.crowd.EntityCreator;
22 |
23 | public class GroupEntityCreator extends EntityCreator
24 | {
25 | public GroupEntityCreator(long directoryId)
26 | {
27 | super(directoryId);
28 | }
29 |
30 | public int minProperties()
31 | {
32 | return 2;
33 | }
34 |
35 | public Object hydrateObject(String[] values)
36 | {
37 | GroupTemplate group = new GroupTemplate(values[1], getDirectoryId(), GroupType.GROUP);
38 |
39 | group.setActive(true);
40 | // description might not be set
41 | if (values.length > 3)
42 | {
43 | group.setDescription(values[3]);
44 | }
45 |
46 | return group;
47 | }
48 |
49 | /**
50 | * Makes a GroupTemplateWithAttributes from a regular Group object.
51 | *
52 | * phpBB does not actually support attributes so we use this to create
53 | * wrapper objects for the places that require it.
54 | *
55 | * @param base The reguler Group object containing all group data.
56 | *
57 | * @return An implementation of GroupWithAttributes, that contains
58 | * all the basic data passed in the parameter.
59 | */
60 | public GroupWithAttributes attachAttributes(Object baseObject)
61 | {
62 | Group base = (Group) baseObject;
63 | GroupTemplateWithAttributes group = new GroupTemplateWithAttributes(base.getName(), getDirectoryId(), GroupType.GROUP);
64 |
65 | group.setDescription(base.getDescription());
66 | group.setName(base.getName());
67 | group.setActive(base.isActive());
68 |
69 | return group;
70 | }
71 | }
--------------------------------------------------------------------------------
/crowd-phpbb-directory-server/src/main/java/com/phpbb/crowd/UserEntityCreator.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 phpBB Ltd.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.phpbb.crowd;
17 |
18 | import com.atlassian.crowd.exception.*;
19 | import com.atlassian.crowd.model.user.*;
20 |
21 | import com.phpbb.crowd.EntityCreator;
22 |
23 | public class UserEntityCreator extends EntityCreator
24 | {
25 | public UserEntityCreator(long directoryId)
26 | {
27 | super(directoryId);
28 | }
29 |
30 | public int minProperties()
31 | {
32 | return 4;
33 | }
34 |
35 | // properties: user_id, username, email, user_type, avatar, firstname, lastname
36 | public Object hydrateObject(String[] values)
37 | {
38 | UserTemplate user = new UserTemplate(values[1], getDirectoryId());
39 |
40 | if (values.length > 6 && values[5] != null && values[6] != null)
41 | {
42 | user.setFirstName(values[5]);
43 | user.setLastName(values[6]);
44 | user.setDisplayName(values[5] + " " + values[6]);
45 | }
46 | else
47 | {
48 | user.setFirstName("");
49 | user.setLastName(values[1]);
50 | user.setDisplayName(values[1]);
51 | }
52 |
53 | user.setName(values[1]);
54 | user.setEmailAddress(values[2]);
55 |
56 | if (values[3].equals("1") || values[3].equals("2"))
57 | {
58 | user.setActive(false);
59 | }
60 | else
61 | {
62 | user.setActive(true);
63 | }
64 |
65 | // avatar might not be set
66 | // if (values.length > 4 && values[4] != null)
67 | // {
68 | // // avatar in values[4]);
69 | // }
70 |
71 | return user;
72 | }
73 |
74 | /**
75 | * Makes a UserTemplateWithAttributes from a regular User object.
76 | *
77 | * phpBB does not actually support attributes so we use this to create
78 | * wrapper objects for the places that require it.
79 | *
80 | * @param base The reguler User object containing all user data.
81 | *
82 | * @return An implementation of UserWithAttributes, that contains
83 | * all the basic data passed in the parameter.
84 | */
85 | public UserWithAttributes attachAttributes(Object baseObject)
86 | {
87 | User base = (User) baseObject;
88 |
89 | UserTemplateWithAttributes user = new UserTemplateWithAttributes(base.getName(), getDirectoryId());
90 |
91 | user.setFirstName(base.getFirstName());
92 | user.setLastName(base.getLastName());
93 | user.setDisplayName(base.getDisplayName());
94 | user.setName(base.getName());
95 | user.setEmailAddress(base.getEmailAddress());
96 | user.setActive(base.isActive());
97 |
98 | return user;
99 | }
100 | }
--------------------------------------------------------------------------------
/crowd-phpbb-directory-server/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 | 4.0.0
8 | phpbb
9 | phpbbauth
10 | 1.3-SNAPSHOT
11 |
12 |
13 | phpBB
14 | https://www.phpBB.com/
15 |
16 |
17 | phpbbauth
18 | This is the phpbb:phpbbauth plugin for Atlassian Crowd.
19 | jar
20 |
21 |
22 |
23 |
24 | com.atlassian.crowd
25 | atlassian-crowd
26 | ${crowd.version}
27 | pom
28 | import
29 |
30 |
31 |
32 |
33 |
34 |
35 | com.atlassian.crowd
36 | crowd-api
37 |
38 |
39 |
40 |
41 |
42 |
43 | com.atlassian.maven.plugins
44 | crowd-maven-plugin
45 | ${amps.version}
46 | true
47 |
48 | ${crowd.version}
49 | ${crowd.data.version}
50 | true
51 |
52 |
53 |
54 |
55 | ${atlassian.plugin.key}
56 |
57 |
58 |
59 | com.phpbb.crowd,
60 |
61 |
62 |
63 | *
64 |
65 |
66 | -Xlint:all
67 | true
68 |
69 |
70 |
79 |
80 |
81 |
82 |
83 | 3.3.3
84 | 3.3.3
85 | 8.0.0
86 |
87 | ${project.groupId}.${project.artifactId}
88 | UTF-8
89 | 1.8
90 | 1.8
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/crowd-phpbb-directory-server/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/phpbb-external-auth-api/COPYING:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5 | 675 Mass Ave, Cambridge, MA 02139, USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Library General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 |
--------------------------------------------------------------------------------
/crowd-phpbb-directory-server/src/main/java/com/phpbb/crowd/phpBBDirectoryServer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 phpBB Ltd.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.phpbb.crowd;
17 |
18 | import com.atlassian.crowd.directory.RemoteDirectory;
19 | import com.atlassian.crowd.embedded.api.PasswordCredential;
20 | import com.atlassian.crowd.exception.*;
21 | import com.atlassian.crowd.model.*;
22 | import com.atlassian.crowd.model.user.*;
23 | import com.atlassian.crowd.model.group.*;
24 | import com.atlassian.crowd.model.membership.*;
25 | import com.atlassian.crowd.search.query.membership.*;
26 | import com.atlassian.crowd.search.query.entity.*;
27 | import com.atlassian.crowd.search.query.entity.restriction.*;
28 | import com.atlassian.crowd.search.ReturnType;
29 | import com.atlassian.crowd.search.Entity;
30 | import com.atlassian.crowd.embedded.api.SearchRestriction;
31 | import com.atlassian.crowd.util.BoundedCount;
32 |
33 | import java.rmi.RemoteException;
34 | import java.util.*;
35 |
36 | import org.slf4j.Logger;
37 | import org.slf4j.LoggerFactory;
38 |
39 | import java.io.*;
40 | import java.net.*;
41 |
42 | /**
43 | * The phpBB Directory Server provides an interface to phpBB users and groups.
44 | *
45 | * All access is read-only. The data is retrieved through HTTP requests to a
46 | * special file that has to be dropped into the phpBB root directory.
47 | */
48 | public class phpBBDirectoryServer implements RemoteDirectory
49 | {
50 | private static final Logger log = LoggerFactory.getLogger(phpBBDirectoryServer.class);
51 |
52 | private long directoryId;
53 | private Map attributes;
54 |
55 | private String serverUrl;
56 |
57 | /**
58 | * Constructor of the phpBB Directory Server reads the phpBB board root URL from
59 | * the environment variable CROWD_PHPBB_ROOT_URL.
60 | *
61 | * Make sure to export this variable before starting crowd.
62 | */
63 | public phpBBDirectoryServer()
64 | {
65 | serverUrl = System.getenv("CROWD_PHPBB_ROOT_URL");
66 | }
67 |
68 | @Override
69 | public long getDirectoryId()
70 | {
71 | return directoryId;
72 | }
73 |
74 | @Override
75 | public void setDirectoryId(long directoryId)
76 | {
77 | this.directoryId = directoryId;
78 | }
79 |
80 | public String getName()
81 | {
82 | return "phpbbauth";
83 | }
84 |
85 | @Override
86 | public String getDescriptiveName()
87 | {
88 | return "phpBB Directory Server";
89 | }
90 |
91 | @Override
92 | public void setAttributes(Map attributes)
93 | {
94 | this.attributes = attributes;
95 | }
96 |
97 | @Override
98 | public Set getValues(String name)
99 | {
100 | log.info("crowd-phpbbauth-plugin: getAttributes: " + name);
101 | return new HashSet();
102 | }
103 |
104 | @Override
105 | public String getValue(String name)
106 | {
107 | log.info("crowd-phpbbauth-plugin: getAttribute: " + name);
108 | return "";
109 | }
110 |
111 | @Override
112 | public Set getKeys()
113 | {
114 | log.info("crowd-phpbbauth-plugin: getAttributeNames");
115 | return new HashSet();
116 | }
117 |
118 | public boolean hasAttribute(String name)
119 | {
120 | log.info("crowd-phpbbauth-plugin: hasAttribute: " + name);
121 | return false;
122 | }
123 |
124 | @Override
125 | public boolean isEmpty()
126 | {
127 | return true;
128 | }
129 |
130 | @Override
131 | public User findUserByName(String name)
132 | throws UserNotFoundException
133 | {
134 | log.info("crowd-phpbbauth-plugin: findUserByName: " + name);
135 |
136 | SearchRestriction searchRestriction = new TermRestriction(new PropertyImpl("name", String.class), MatchMode.EXACTLY_MATCHES, name);
137 | UserQuery query = new UserQuery(User.class, searchRestriction, 0, 1); // start, max
138 |
139 | List list = new ArrayList();
140 | searchEntities("searchUsers", new UserEntityCreator(getDirectoryId()), query, list);
141 |
142 | if (list.size() == 0)
143 | {
144 | throw new UserNotFoundException(name);
145 | }
146 |
147 | return (User) list.get(0);
148 |
149 | }
150 |
151 | @Override
152 | public User findUserByExternalId(String externalId)
153 | throws UserNotFoundException, OperationFailedException
154 | {
155 | log.info("crowd-phpbbauth-plugin: findUserByExternalId: " + externalId);
156 | throw new OperationFailedException();
157 | }
158 |
159 | @Override
160 | public UserWithAttributes findUserWithAttributesByName(String name)
161 | throws UserNotFoundException, OperationFailedException
162 | {
163 | log.info("crowd-phpbbauth-plugin: findUserWithAttributesByName: " + name);
164 |
165 | // no attributes support, so just fake it
166 | User user = findUserByName(name);
167 |
168 | return (UserWithAttributes) new UserEntityCreator(getDirectoryId()).attachAttributes(user);
169 | }
170 |
171 | /**
172 | * Authenticates a user against the phpBB authentication method.
173 | *
174 | * Username and password are sent in a urlencoded POST request.
175 | * The request returns either an error line and an error message
176 | * or success and the data required to create a UserTemplate
177 | * object. phpBB still counts login attempts, so brute force is not
178 | * possible regardless of how this method is used.
179 | *
180 | * @param name The username.
181 | * @param credential Credential object containing the password.
182 | *
183 | * @return A UserTemplate object, which
184 | * implements the User interface.
185 | *
186 | * @throws InactiveAccountException Account inactive or login
187 | * attempts exceeded.
188 | * @throws InvalidAuthenticationException Invalid username or password.
189 | * @throws ObjectNotFoundException Any other errors
190 | */
191 | @Override
192 | public User authenticate(String name, PasswordCredential credential)
193 | throws UserNotFoundException, InactiveAccountException, InvalidAuthenticationException, ExpiredCredentialException, OperationFailedException
194 | {
195 | HashMap params = new HashMap();
196 | params.put("action", "authenticate");
197 | params.put("name", name);
198 | params.put("credential", credential.getCredential());
199 |
200 | ArrayList result = sendPostRequest(params);
201 |
202 | if (result.size() < 2)
203 | {
204 | throw new UserNotFoundException(name);
205 | }
206 |
207 | log.info("crowd-phpbbauth-plugin: authenticate: " + name);
208 | log.info("crowd-phpbbauth-plugin: result: " + result.get(0));
209 | log.info("crowd-phpbbauth-plugin: result: " + result.get(1));
210 |
211 | if (!result.get(0).equals("success"))
212 | {
213 | //NOTE: These messages will only appear in Crowd. Jira will ignore this and simply show "Sorry, your username and password are incorrect"
214 | String error = result.get(1);
215 |
216 | if (error.equals("LOGIN_ERROR_ATTEMPTS"))
217 | {
218 | throw new InvalidAuthenticationException("Too many failed logins on forum.");
219 | }
220 |
221 | if (error.equals("ACTIVE_ERROR"))
222 | {
223 | throw new InactiveAccountException("Account is inactive.");
224 | }
225 |
226 | throw new InvalidAuthenticationException("Username or password are incorrect.");
227 | }
228 |
229 | UserEntityCreator entityCreator = new UserEntityCreator(getDirectoryId());
230 | try
231 | {
232 | User user = (User) entityCreator.fromLine(result.get(1));
233 | return user;
234 | }
235 | catch (ObjectNotFoundException e)
236 | {
237 | throw new UserNotFoundException(name);
238 | }
239 | }
240 |
241 | @Override
242 | public User addUser(UserTemplate user, PasswordCredential credential)
243 | throws OperationFailedException
244 | {
245 | log.info("addUser: " + user.toString());
246 | throw new OperationFailedException();
247 | }
248 |
249 | @Override
250 | public UserWithAttributes addUser(UserTemplateWithAttributes user, PasswordCredential credential)
251 | throws OperationFailedException {
252 | log.info("addUser (withAttributes): " + user.toString());
253 | throw new OperationFailedException();
254 | }
255 |
256 | @Override
257 | public User updateUser(UserTemplate user)
258 | throws OperationFailedException
259 | {
260 | log.info("updateUser: " + user.toString());
261 | throw new OperationFailedException();
262 | }
263 |
264 | @Override
265 | public void updateUserCredential(String username, PasswordCredential credential)
266 | throws OperationFailedException
267 | {
268 | log.info("updateUserCredential: " + username);
269 | throw new OperationFailedException();
270 | }
271 |
272 | @Override
273 | public User renameUser(String oldName, String newName)
274 | throws OperationFailedException, InvalidUserException
275 | {
276 | log.info("renameUser: " + oldName);
277 | throw new OperationFailedException();
278 | }
279 |
280 | @Override
281 | public void storeUserAttributes(String username, Map> attributes)
282 | throws OperationFailedException
283 | {
284 | log.info("storeUserAttributes: " + username);
285 | throw new OperationFailedException();
286 | }
287 |
288 | @Override
289 | public void removeUserAttributes(String username, String attributeName)
290 | throws OperationFailedException
291 | {
292 | log.info("removeUserAttributes: " + username);
293 | throw new OperationFailedException();
294 | }
295 |
296 | @Override
297 | public void removeUser(String name)
298 | throws OperationFailedException
299 | {
300 | log.info("removeUser: " + name);
301 | throw new OperationFailedException();
302 | }
303 |
304 | @Override
305 | public List searchUsers(EntityQuery query)
306 | {
307 | log.info("crowd-phpbbauth-plugin: searchUsers - " + query.toString());
308 |
309 | List list = new ArrayList();
310 | searchEntities("searchUsers", new UserEntityCreator(getDirectoryId()), query, list);
311 |
312 | return list;
313 | }
314 |
315 | @Override
316 | public Group findGroupByName(String name)
317 | throws GroupNotFoundException
318 | {
319 | log.info("crowd-phpbbauth-plugin: findGroupByName: " + name);
320 |
321 | SearchRestriction searchRestriction = new TermRestriction(new PropertyImpl("name", String.class), MatchMode.EXACTLY_MATCHES, name);
322 | GroupQuery query = new GroupQuery(Group.class, GroupType.GROUP, searchRestriction, 0, 1); // start, max
323 |
324 | List list = new ArrayList();
325 | searchEntities("searchGroups", new GroupEntityCreator(getDirectoryId()), query, list);
326 |
327 | if (list.size() == 0)
328 | {
329 | throw new GroupNotFoundException(name);
330 | }
331 |
332 | return (Group) list.get(0);
333 | }
334 |
335 | @Override
336 | public GroupWithAttributes findGroupWithAttributesByName(String name)
337 | throws OperationFailedException, GroupNotFoundException
338 | {
339 | log.info("crowd-phpbbauth-plugin: findGroupWithAttributesByName: " + name);
340 |
341 | // no attributes support, so just fake it
342 | Group group = findGroupByName(name);
343 |
344 | return (GroupWithAttributes) new GroupEntityCreator(getDirectoryId()).attachAttributes(group);
345 | }
346 |
347 | @Override
348 | public Group addGroup(GroupTemplate group)
349 | throws OperationFailedException
350 | {
351 | log.info("addGroup: " + group.toString());
352 | throw new OperationFailedException();
353 | }
354 |
355 | @Override
356 | public Group updateGroup(GroupTemplate group)
357 | throws ReadOnlyGroupException
358 | {
359 | log.info("updateGroup: " + group.toString());
360 | throw new ReadOnlyGroupException(group.getName());
361 | }
362 |
363 | @Override
364 | public Group renameGroup(String oldName, String newName)
365 | throws OperationFailedException
366 | {
367 | log.info("renameGroup: " + oldName + " to " + newName);
368 | throw new OperationFailedException();
369 | }
370 |
371 | @Override
372 | public void storeGroupAttributes(String groupName, Map> attributes)
373 | throws OperationFailedException
374 | {
375 | log.info("storeGroupAttributes: " + groupName);
376 | throw new OperationFailedException();
377 | }
378 |
379 | @Override
380 | public void removeGroupAttributes(String groupName, String attributeName)
381 | throws OperationFailedException
382 | {
383 | log.info("removeGroupAttributes: " + groupName);
384 | throw new OperationFailedException();
385 | }
386 |
387 | @Override
388 | public void removeGroup(String name)
389 | throws ReadOnlyGroupException
390 | {
391 | log.info("removeGroup: " + name);
392 | throw new ReadOnlyGroupException(name);
393 | }
394 |
395 | @Override
396 | public List searchGroups(EntityQuery query)
397 | {
398 | log.info("crowd-phpbbauth-plugin: searchGroups - " + query.toString());
399 |
400 | List list = new ArrayList();
401 | searchEntities("searchGroups", new GroupEntityCreator(getDirectoryId()), query, list);
402 |
403 | return list;
404 | }
405 |
406 | @Override
407 | public boolean isUserDirectGroupMember(String username, String groupName)
408 | {
409 | log.info("crowd-phpbbauth-plugin: isUserDirectGroupMember: " + username + ", " + groupName);
410 | EntityCreator creator;
411 | List list;
412 | UserQuery userQuery;
413 |
414 | SearchRestriction searchRestrictionUser = new TermRestriction(
415 | new PropertyImpl("name", String.class),
416 | MatchMode.EXACTLY_MATCHES,
417 | username
418 | );
419 |
420 | SearchRestriction searchRestrictionGroup = new TermRestriction(
421 | new PropertyImpl("groupname", String.class),
422 | MatchMode.EXACTLY_MATCHES,
423 | groupName
424 | );
425 |
426 | SearchRestriction searchRestriction = new BooleanRestrictionImpl(
427 | BooleanRestriction.BooleanLogic.AND,
428 | searchRestrictionUser,
429 | searchRestrictionGroup
430 | );
431 |
432 | list = new ArrayList();
433 | userQuery = new UserQuery(String.class, searchRestriction, 0, 1); // start, max
434 |
435 | searchEntities("userMemberships", null, userQuery, list);
436 |
437 | if (list.size() > 0)
438 | {
439 | return true;
440 | }
441 | return false;
442 | }
443 |
444 | /**
445 | * No nested group support, so a group can never be a direct member.
446 | *
447 | * @return Always false.
448 | */
449 | @Override
450 | public boolean isGroupDirectGroupMember(String childGroup, String parentGroup)
451 | {
452 | log.info("crowd-phpbbauth-plugin: isGroupDirectGroupMember");
453 | return false;
454 | }
455 |
456 | @Override
457 | public BoundedCount countDirectMembersOfGroup(String groupName, int querySizeHint)
458 | throws OperationFailedException
459 | {
460 | log.info("countDirectMembersOfGroup: " + groupName);
461 | throw new OperationFailedException();
462 | }
463 |
464 | @Override
465 | public void addUserToGroup(String username, String groupName)
466 | throws ReadOnlyGroupException
467 | {
468 | log.info("addUserToGroup: " + username + ", " + groupName);
469 | throw new ReadOnlyGroupException(groupName);
470 | }
471 |
472 | @Override
473 | public void addGroupToGroup(String childGroup, String parentGroup)
474 | throws ReadOnlyGroupException
475 | {
476 | log.info("addGroupToGroup: " + childGroup + ", " + parentGroup);
477 | throw new ReadOnlyGroupException(parentGroup);
478 | }
479 |
480 | @Override
481 | public void removeUserFromGroup(String username, String groupName)
482 | throws ReadOnlyGroupException
483 | {
484 | log.info("removeUserFromGroup: " + username + ", " + groupName);
485 | throw new ReadOnlyGroupException(groupName);
486 | }
487 |
488 | @Override
489 | public void removeGroupFromGroup(String childGroup, String parentGroup)
490 | throws MembershipNotFoundException
491 | {
492 | log.info("removeGroupFromGroup: " + childGroup + ", " + parentGroup);
493 | throw new MembershipNotFoundException(childGroup, parentGroup);
494 | }
495 |
496 | @Override
497 | public List searchGroupRelationships(MembershipQuery query)
498 | {
499 | log.info("crowd-phpbbauth-plugin: searchGroupRelationships - " + query.toString());
500 |
501 | EntityCreator creator;
502 | List list;
503 | EntityQuery entityQuery;
504 | String action;
505 |
506 | SearchRestriction searchRestriction = new TermRestriction(
507 | new PropertyImpl("name", String.class),
508 | MatchMode.EXACTLY_MATCHES,
509 | query.getEntityNamesToMatch().iterator().next() // We only operate on single search terms, so grab the first
510 | );
511 |
512 | if (query.getEntityToMatch().getEntityType() != Entity.GROUP)
513 | {
514 | action = "userMemberships";
515 | entityQuery = new UserQuery(User.class, searchRestriction, query.getStartIndex(), query.getMaxResults());
516 | }
517 | else
518 | {
519 | action = "groupMembers";
520 | entityQuery = new GroupQuery(Group.class, GroupType.GROUP, searchRestriction, query.getStartIndex(), query.getMaxResults());
521 | }
522 |
523 | if (query.getEntityToReturn().getEntityType() == Entity.GROUP)
524 | {
525 | creator = new GroupEntityCreator(getDirectoryId());
526 | list = new ArrayList();
527 | }
528 | else
529 | {
530 | creator = new UserEntityCreator(getDirectoryId());
531 | list = new ArrayList();
532 | }
533 |
534 | log.info("crowd-phpbbauth-plugin: entity to return: " + query.getEntityToReturn().toString());
535 | log.info("crowd-phpbbauth-plugin: returnType: " + query.getReturnType().toString());
536 |
537 | searchEntities(action, creator, entityQuery, list);
538 |
539 | if (query.getReturnType() == String.class)
540 | {
541 | ArrayList stringList = new ArrayList();
542 | for (Iterator it = list.iterator(); it.hasNext(); )
543 | {
544 | Object x = it.next();
545 | if (x instanceof GroupTemplate)
546 | {
547 | stringList.add(((GroupTemplate) x).getName());
548 | }
549 | else
550 | {
551 | stringList.add(((UserTemplate) x).getName());
552 | }
553 | }
554 | return stringList;
555 | }
556 |
557 | return list;
558 | }
559 |
560 | @Override
561 | public void testConnection()
562 | throws OperationFailedException
563 | {
564 | // could implement a simple http request here
565 | }
566 |
567 | @Override
568 | public void expireAllPasswords()
569 | throws OperationFailedException
570 | {
571 | log.info("expireAllPasswords");
572 | throw new OperationFailedException();
573 | }
574 |
575 | /**
576 | * phpBB does not support nested Groups
577 | *
578 | * @return Always false.
579 | */
580 | @Override
581 | public boolean supportsNestedGroups()
582 | {
583 | return false;
584 | }
585 |
586 | @Override
587 | public boolean supportsPasswordExpiration()
588 | {
589 | return false;
590 | }
591 |
592 | @Override
593 | public boolean supportsSettingEncryptedCredential()
594 | {
595 | return false;
596 | }
597 |
598 | @Override
599 | public RemoteDirectory getAuthoritativeDirectory()
600 | {
601 | return (RemoteDirectory) this;
602 | }
603 |
604 | @Override
605 | public Iterable getMemberships()
606 | throws OperationFailedException
607 | {
608 | log.info("crowd-phpbbauth-plugin: getMemberships");
609 | throw new OperationFailedException();
610 | }
611 |
612 | @Override
613 | public boolean isRolesDisabled()
614 | {
615 | return true;
616 | }
617 |
618 | @Override
619 | public boolean supportsInactiveAccounts()
620 | {
621 | return true;
622 | }
623 |
624 | protected void searchEntities(String action, EntityCreator entityCreator, EntityQuery query, List list)
625 | {
626 | log.info("searchEntities: " + action + ", " + query.toString());
627 | HashMap params = new HashMap();
628 | params.put("action", action);
629 | params.put("start", new Integer(query.getStartIndex()).toString());
630 | params.put("max", new Integer(query.getMaxResults()).toString());
631 |
632 | String returnType = (query.getReturnType() == String.class) ? "NAME" : query.getReturnType().toString();
633 |
634 | if (query.getReturnType() == User.class || query.getReturnType() == Group.class || query.getReturnType() == GroupWithAttributes.class)
635 | {
636 | returnType = "ENTITY";
637 | }
638 |
639 | params.put("returnType", returnType); // NAME or ENTITY
640 |
641 | SearchRestriction restriction = query.getSearchRestriction();
642 | if (restriction != null)
643 | {
644 | params.put("restriction", restrictionToJson(restriction));
645 | }
646 |
647 | ArrayList result = sendPostRequest(params);
648 | if (result.size() > 0)
649 | {
650 | log.info("crowd-phpbbauth-plugin: returnType: " + returnType);
651 | log.info("crowd-phpbbauth-plugin: Num results: " + String.valueOf(result.size()));
652 | log.info("crowd-phpbbauth-plugin: result[0]: " + result.get(0));
653 | }
654 |
655 | for (Iterator it = result.iterator(); it.hasNext(); )
656 | {
657 | String line = (String) it.next();
658 |
659 | if (query.getReturnType() != String.class)
660 | {
661 | try
662 | {
663 | list.add(entityCreator.fromLine(line));
664 | }
665 | catch (ObjectNotFoundException e)
666 | {
667 | log.info("Exception: " + e.toString());
668 | }
669 | }
670 | else
671 | {
672 | list.add(line);
673 | }
674 | }
675 | }
676 |
677 | protected String restrictionToJson(SearchRestriction restriction)
678 | {
679 | if (restriction instanceof NullRestriction)
680 | {
681 | return "null";
682 | }
683 | else if (restriction instanceof TermRestriction)
684 | {
685 | TermRestriction termRestriction = (TermRestriction) restriction;
686 | String value = "";
687 | if (termRestriction.getValue() != null)
688 | {
689 | value = termRestriction.getValue().toString();
690 | }
691 |
692 | return "{\"mode\": \"" + termRestriction.getMatchMode().toString() + "\", \"property\": \"" + escape(termRestriction.getProperty().getPropertyName()) + "\", \"value\": \"" + escape(value) + "\"}";
693 | }
694 | else if (restriction instanceof BooleanRestrictionImpl)
695 | {
696 | BooleanRestrictionImpl multiTermRestriction = (BooleanRestrictionImpl) restriction;
697 | Collection restrictions = multiTermRestriction.getRestrictions();
698 |
699 | String result = "{\"boolean\": \"" + multiTermRestriction.getBooleanLogic().toString() + "\", \"terms\": [";
700 |
701 | for (Iterator it = restrictions.iterator(); it.hasNext(); )
702 | {
703 | result += restrictionToJson((SearchRestriction) it.next());
704 |
705 | if (it.hasNext())
706 | {
707 | result += ", ";
708 | }
709 | }
710 | result += "]}";
711 |
712 | return result;
713 | }
714 |
715 | return "null";
716 | }
717 |
718 | /**
719 | * Escapes a string for use in a JSON string.
720 | *
721 | * @param source The unescaped input string.
722 | *
723 | * @return Source string with backslashes and quotes escaped.
724 | */
725 | protected String escape(String source)
726 | {
727 | String result;
728 |
729 | // double backslashes because of java string, and those doubled again
730 | // because it's a regex. so this is really just
731 | // (1) \ => \\
732 | // (2) " => \"
733 | result = source.replaceAll("\\\\", "\\\\\\\\");
734 | result = result.replaceAll("\"", "\\\\\\\"");
735 |
736 | return result;
737 | }
738 |
739 | protected ArrayList sendPostRequest(Map params)
740 | {
741 | String data = "";
742 | ArrayList result = new ArrayList();
743 |
744 | try
745 | {
746 | for (Map.Entry entry : params.entrySet())
747 | {
748 | data += URLEncoder.encode(entry.getKey(), "UTF-8");
749 | data += "=";
750 | data += URLEncoder.encode(entry.getValue(), "UTF-8");
751 | data += "&";
752 | }
753 | String api_url = serverUrl;
754 | if (serverUrl.charAt(serverUrl.length() - 1) != '/')
755 | {
756 | api_url += "/";
757 | }
758 |
759 | URL url = new URL(api_url + "auth_api.php");
760 |
761 | URLConnection connection = url.openConnection();
762 | connection.setDoOutput(true);
763 |
764 | OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
765 |
766 | writer.write(data);
767 | writer.flush();
768 |
769 | BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
770 |
771 | String line;
772 | while ((line = reader.readLine()) != null)
773 | {
774 | result.add(line);
775 | }
776 |
777 | writer.close();
778 | reader.close();
779 | }
780 | catch (Exception e)
781 | {
782 | log.error("crowd-phpbbauth-plugin: HTTP request: " + e);
783 | }
784 |
785 | return result;
786 | }
787 | }
788 |
--------------------------------------------------------------------------------
/phpbb-external-auth-api/auth_api.php:
--------------------------------------------------------------------------------
1 | register();
37 |
38 | $phpbb_config_php_file = new \phpbb\config_php_file($phpbb_root_path, $phpEx);
39 | extract($phpbb_config_php_file->get_all());
40 |
41 |
42 |
43 | // Before we actually initialise all files, maybe we could simply return the important part quickly?
44 | if ($api_config['api_cache_users'] && !empty($_POST['action']) && $_POST['action'] == 'searchUsers')
45 | {
46 | $result = _api_get_cached_user($root_path . 'user_cache/', $api_config['api_cache_users']);
47 |
48 | if ($result !== false)
49 | {
50 | echo $result;
51 | garbage_collection();
52 | exit_handler();
53 | exit;
54 | }
55 | }
56 |
57 | require($phpbb_root_path . 'config.' . $phpEx);
58 |
59 | if (!defined('PHPBB_INSTALLED') || empty($dbms) || empty($acm_type))
60 | {
61 | exit;
62 | }
63 |
64 | // Include files
65 | require($phpbb_root_path . 'includes/constants.' . $phpEx);
66 | require($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx);
67 | require($phpbb_root_path . 'includes/functions.' . $phpEx);
68 | require($phpbb_root_path . 'includes/functions_user.' . $phpEx);
69 | include($phpbb_root_path . 'includes/functions_compatibility.' . $phpEx);
70 |
71 | $phpbb_class_loader_ext = new \phpbb\class_loader('\\', "{$phpbb_root_path}ext/", $phpEx);
72 | $phpbb_class_loader_ext->register();
73 |
74 | // Set up container
75 | try
76 | {
77 | $phpbb_container_builder = new \phpbb\di\container_builder($phpbb_root_path, $phpEx);
78 | $phpbb_container = $phpbb_container_builder->with_config($phpbb_config_php_file)->get_container();
79 | }
80 | catch (InvalidArgumentException $e)
81 | {
82 | if (PHPBB_ENVIRONMENT !== 'development')
83 | {
84 | trigger_error(
85 | 'The requested environment ' . PHPBB_ENVIRONMENT . ' is not available.',
86 | E_USER_ERROR
87 | );
88 | }
89 | else
90 | {
91 | throw $e;
92 | }
93 | }
94 |
95 | $phpbb_class_loader->set_cache($phpbb_container->get('cache.driver'));
96 | $phpbb_class_loader_ext->set_cache($phpbb_container->get('cache.driver'));
97 |
98 | $phpbb_container->get('dbal.conn')->set_debug_sql_explain($phpbb_container->getParameter('debug.sql_explain'));
99 | $phpbb_container->get('dbal.conn')->set_debug_load_time($phpbb_container->getParameter('debug.load_time'));
100 | require($phpbb_root_path . 'includes/compatibility_globals.' . $phpEx);
101 |
102 | register_compatibility_globals();
103 |
104 | $user->session_begin(false);
105 |
106 | // Re-enable superglobals. This should be rewritten at some point to use the request system
107 | $request->enable_super_globals();
108 |
109 |
110 | // Initialize auth API
111 | $api = new phpbb_auth_api($api_config);
112 |
113 | $action = basename(request_var('action', ''));
114 |
115 | // First wanted to implement this with __call(), but then there is no way for auto completion in modern Editors
116 | if ($api->init($action))
117 | {
118 | $api->$action();
119 | }
120 |
121 | echo $api->get_output();
122 | $api->close();
123 |
124 | if (!empty($cache))
125 | {
126 | $cache->unload();
127 | }
128 | $db->sql_close();
129 |
130 | exit;
131 |
132 | /**
133 | * Authentication to Crowd
134 | */
135 | class phpbb_auth_api
136 | {
137 | protected $debug_file = false;
138 |
139 | protected $fp = false;
140 | protected $output = array();
141 |
142 | protected $user_query_sql = false;
143 |
144 | // For faster access
145 | protected $special_groups = array();
146 | protected $special_groups_reverse = array();
147 |
148 | protected $config = array();
149 |
150 | public function __construct($config)
151 | {
152 | $this->config = $config;
153 | $this->debug_file = $config['debug_api'];
154 |
155 | // Special group name mapping
156 | $this->special_groups = array(
157 | 'ADMINISTRATORS' => 'Administrators',
158 | 'BOTS' => 'Bots',
159 | 'GUESTS' => 'Guests',
160 | 'REGISTERED' => 'Registered users',
161 | 'REGISTERED_COPPA' => 'Registered COPPA users',
162 | 'GLOBAL_MODERATORS' => 'Global moderators',
163 | 'NEWLY_REGISTERED' => 'Newly registered users',
164 | );
165 |
166 | $this->special_groups = array_change_key_case($this->special_groups, CASE_LOWER);
167 |
168 | $this->special_groups_reverse = array_flip($this->special_groups);
169 | $this->special_groups_reverse = array_change_key_case($this->special_groups_reverse, CASE_LOWER);
170 | }
171 |
172 | public function implemented()
173 | {
174 | return array(
175 | 'authenticate',
176 | 'searchUsers',
177 | 'searchGroups',
178 | 'groupMembers',
179 | 'UserMemberships',
180 | );
181 | }
182 |
183 | public function init($action)
184 | {
185 | $this->output = array();
186 |
187 | if (!method_exists($this, $action))
188 | {
189 | return false;
190 | }
191 |
192 | if ($this->debug_file && !$this->fp)
193 | {
194 | $this->fp = fopen($this->debug_file, 'a');
195 | $this->debug('INIT', '');
196 | $this->debug('post', $_POST);
197 | }
198 | return true;
199 | }
200 |
201 | public function close()
202 | {
203 | if ($this->debug_file && $this->fp)
204 | {
205 | $this->debug('output', $this->output);
206 | $this->debug('FINISHED', '');
207 | fclose($this->fp);
208 | }
209 | }
210 |
211 | public function get_output()
212 | {
213 | return implode("\n", $this->output);
214 | }
215 |
216 | public function debug($action, $data)
217 | {
218 | if (!$this->debug_file) return;
219 |
220 | fwrite($this->fp, '[' . date('Y-m-d H:i:s') . '] [' . $action . '] ');
221 | if (is_array($data))
222 | {
223 | if ($this->config['debug_api_limit_output'] && sizeof($data) > $this->config['debug_api_limit_output'])
224 | {
225 | fwrite($this->fp, 'Dataset: ' . sizeof($data) . ' Elements.');
226 | $data = array();
227 | }
228 |
229 | if (!empty($data['credential']))
230 | {
231 | $data['credential'] = '***';
232 | }
233 |
234 | foreach ($data as $key => $element)
235 | {
236 | fwrite($this->fp, "\n[" . date('Y-m-d H:i:s') . '] [' . $key . '] => ' . $element);
237 | }
238 | }
239 | else
240 | {
241 | fwrite($this->fp, $data);
242 | }
243 |
244 | fwrite($this->fp, "\n");
245 | }
246 |
247 | protected function add($line)
248 | {
249 | $this->output[] = $line;
250 | return $this;
251 | }
252 |
253 | protected function groups_query($sql_prefix = '', $sql_alias = '')
254 | {
255 | if (empty($this->config['exclude_groups']))
256 | {
257 | return '';
258 | }
259 |
260 | $sql = ($sql_prefix) ? ' ' . $sql_prefix . ' ' : '';
261 | $sql .= ($sql_alias) ? $sql_alias . '.' : '';
262 | $sql .= 'group_id NOT IN (' . implode(', ', array_map('intval', $this->config['exclude_groups'])) . ')';
263 |
264 | return $sql;
265 | }
266 |
267 | protected function users_query($sql_prefix = '', $sql_alias = '')
268 | {
269 | global $db;
270 |
271 | if ($this->user_query_sql === false)
272 | {
273 | // Get banned user ids
274 | $banned_user_ids = array();
275 |
276 | if ($this->config['exclude_banned_users'])
277 | {
278 | $sql = 'SELECT ban_userid FROM ' . BANLIST_TABLE . '
279 | WHERE ban_userid <> 0
280 | AND ban_exclude = 0';
281 | $result = $db->sql_query($sql);
282 | while ($row = $db->sql_fetchrow($result))
283 | {
284 | $banned_user_ids[] = (int) $row['ban_userid'];
285 | }
286 | $db->sql_freeresult($result);
287 | }
288 |
289 | // Check for last login date. ;)
290 | // Use the same timestamp for a day, to let the query be cached later maybe. :)
291 | if ($this->config['last_login_period'])
292 | {
293 | $last_logged_in = strtotime(date('Y-m-d')) - (int) ($this->config['last_login_period'] * 24 * 60 * 60);
294 | }
295 | else
296 | {
297 | $last_logged_in = 0;
298 | }
299 |
300 | $this->user_query_sql = array();
301 |
302 | if (sizeof($banned_user_ids))
303 | {
304 | $this->user_query_sql[] = array('key' => 'user_id', 'query' => 'NOT IN (' . implode(', ', $banned_user_ids) . ')');
305 | }
306 |
307 | if ($last_logged_in)
308 | {
309 | $include_user_ids = isset($this->config['include_user_ids']) ? $this->config['include_user_ids'] : array();
310 | //$this->user_query_sql[] = array('key' => 'user_lastvisit', 'query' => ' > ' . $last_logged_in);
311 |
312 | $user_query_sql_next = array(
313 | array('key' => 'user_lastvisit', 'query' => ' > ' . $last_logged_in),
314 | array('key' => 'user_regdate', 'query' => ' > ' . $last_logged_in), // for users with user_lastvisit = 0
315 | );
316 |
317 | if ($include_user_ids)
318 | {
319 | $user_query_sql_next[] = array('key' => 'user_id', 'query' => ' IN (' . implode(', ', $include_user_ids) . ')');
320 | }
321 |
322 | $this->user_query_sql[] = $user_query_sql_next;
323 | }
324 | }
325 |
326 | // Return correct query
327 | if (empty($this->user_query_sql))
328 | {
329 | return '';
330 | }
331 |
332 | $sql_ary = array();
333 | foreach ($this->user_query_sql as $query)
334 | {
335 | if (!isset($query[0]))
336 | {
337 | $query = array($query);
338 | }
339 | $or_ary = array();
340 | foreach ($query as $or_clause)
341 | {
342 | $or_ary[] = (($sql_alias) ? $sql_alias . '.' : '') . $or_clause['key'] . ' ' . $or_clause['query'];
343 | }
344 | $sql_ary[] = '( ' . implode(' OR ', $or_ary) . ' ) ';
345 | }
346 |
347 | $q = (($sql_prefix) ? ' ' . $sql_prefix . ' ' : '') . implode(' AND ', $sql_ary);
348 | // call $this->debug ;) See crowd logs for what can happen if there are php notices here
349 | //if ($this->fp) fwrite($this->fp, $q."\n");
350 | return $q;
351 | }
352 |
353 | protected function get_user_name($user_name)
354 | {
355 | $user_name = html_entity_decode($user_name, ENT_COMPAT, 'UTF-8');
356 |
357 | if (preg_match('/[\x01-\x08]/', $user_name))
358 | {
359 | return false;
360 | }
361 |
362 | return $user_name;
363 | }
364 |
365 | // not implemented
366 | public function findUserByName()
367 | {
368 | $name = request_var('name', '', true);
369 | $this->add_line('not implemented');
370 | }
371 |
372 | public function authenticate()
373 | {
374 | global $db, $auth, $config, $phpbb_container;
375 |
376 | $username = request_var('name', '', true);
377 | $password = request_var('credential', '', true);
378 | $err = '';
379 |
380 | /* @var $provider_collection \phpbb\auth\provider_collection */
381 | $provider_collection = $phpbb_container->get('auth.provider_collection');
382 | $provider = $provider_collection->get_provider();
383 | $result = $provider->login($username, $password);
384 |
385 | if (isset($result['user_row']['user_id']))
386 | {
387 | $sql = 'SELECT ban_userid FROM ' . BANLIST_TABLE . '
388 | WHERE ban_userid = ' . (int) $result['user_row']['user_id'];
389 | $ban_result = $db->sql_query($sql);
390 | $row = $db->sql_fetchrow($ban_result);
391 | $db->sql_freeresult($ban_result);
392 |
393 | if ($row)
394 | {
395 | $this->add('error')->add('You are banned');
396 | return;
397 | }
398 | }
399 |
400 | // The result parameter is always an array, holding the relevant information...
401 | if ($result['status'] == LOGIN_SUCCESS)
402 | {
403 | // get avatar url
404 | $sql = 'SELECT u.user_avatar, u.user_avatar_type, u.user_avatar_width, u.user_avatar_height';
405 |
406 | // get first/last name
407 | if ($this->config['firstname_column'] && $this->config['lastname_column'])
408 | {
409 | $sql .= ', pf.pf_' . $this->config['firstname_column'] . ' as firstname, pf.pf_' . $this->config['lastname_column'] . ' as lastname';
410 | }
411 |
412 | $sql .= ' FROM ' . USERS_TABLE . ' u';
413 |
414 | if ($this->config['firstname_column'] && $this->config['lastname_column'])
415 | {
416 | $sql .= ' LEFT JOIN ' . PROFILE_FIELDS_DATA_TABLE . ' pf ON (u.user_id = pf.user_id)';
417 | }
418 |
419 | $sql .= 'WHERE u.user_id = ' . $result['user_row']['user_id'];
420 | $sql_result = $db->sql_query($sql);
421 | $row = $db->sql_fetchrow($sql_result);
422 | $db->sql_freeresult($sql_result);
423 |
424 | $result['user_row']['user_avatar'] = $row['user_avatar'];
425 | $result['user_row']['user_avatar_type'] = $row['user_avatar_type'];
426 | $result['user_row']['user_avatar_width'] = $row['user_avatar_width'];
427 | $result['user_row']['user_avatar_height'] = $row['user_avatar_height'];
428 |
429 | if ($this->config['firstname_column'] && $this->config['lastname_column'])
430 | {
431 | $result['user_row']['firstname'] = $row['firstname'];
432 | $result['user_row']['lastname'] = $row['lastname'];
433 | }
434 |
435 | $this->add('success')->add($this->user_row_line($result['user_row']));
436 | return;
437 | }
438 | else
439 | {
440 | // Failures
441 | switch ($result['status'])
442 | {
443 | case LOGIN_BREAK:
444 | $this->add('error')->add($result['error_msg']);
445 | return;
446 | break;
447 |
448 | case LOGIN_ERROR_ATTEMPTS:
449 | // should we really error here?
450 | // but if the external system does not protect from brute force
451 | // not throwing an error here is potentially dangerous
452 | $this->add('error')->add($result['error_msg']);
453 | return;
454 | break;
455 |
456 | case LOGIN_ERROR_PASSWORD_CONVERT:
457 | // can only tell the person to go back to the forum to get a new password
458 | // unlikely to happen anyway.
459 | $this->add('error')->add($result['error_msg']);
460 | return;
461 | break;
462 |
463 | // Username, password, etc...
464 | default:
465 | $this->add('error')->add($result['error_msg']);
466 | return;
467 | break;
468 | }
469 | }
470 |
471 | $this->add('error')->add('Unexpected result');
472 | }
473 |
474 | public function searchUsers()
475 | {
476 | global $db;
477 |
478 | $start = request_var('start', 0);
479 | $max = request_var('max', 0);
480 | $return_type = request_var('returnType', ''); // NAME or ENTITY
481 | $restriction = html_entity_decode(request_var('restriction', '', true), ENT_COMPAT, 'UTF-8');
482 |
483 | $searchRestriction = new SearchRestriction($this,
484 | $restriction,
485 | // Is it safe to assume our directory will only search for name? ;)
486 | array(
487 | 'email' => 'u.user_email',
488 | 'name' => 'u.username_clean',
489 | 'active' => 'u.user_type'
490 | ));
491 |
492 | $sql = 'SELECT u.user_id, u.username, u.user_type, u.user_email, u.user_avatar, u.user_avatar_type, u.user_avatar_width, u.user_avatar_width';
493 |
494 | if ($this->config['firstname_column'] && $this->config['lastname_column'])
495 | {
496 | $sql .= ', pf.pf_' . $this->config['firstname_column'] . ' as firstname, pf.pf_' . $this->config['lastname_column'] . ' as lastname';
497 | }
498 |
499 | $sql .= ' FROM ' . USERS_TABLE . ' u';
500 |
501 | if ($this->config['firstname_column'] && $this->config['lastname_column'])
502 | {
503 | $sql .= ' LEFT JOIN ' . PROFILE_FIELDS_DATA_TABLE . ' pf ON (u.user_id = pf.user_id)';
504 | }
505 |
506 | $sql .= ' WHERE u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')';
507 | $sql .= $this->users_query('AND', 'u');
508 | $sql .= $searchRestriction->getWhere();
509 | $result = $db->sql_query_limit($sql, $max, $start);
510 |
511 | while ($row = $db->sql_fetchrow($result))
512 | {
513 | $line = ($return_type == 'ENTITY') ? $this->user_row_line($row) : $this->get_user_name($row['username']);
514 |
515 | if ($line !== false)
516 | {
517 | $this->add($line);
518 | }
519 | }
520 | $db->sql_freeresult($result);
521 |
522 | if ($this->config['api_cache_users'] && $max == 1 && $start == 0 && $return_type == 'ENTITY')
523 | {
524 | global $root_path;
525 | _api_set_cached_user($root_path . 'user_cache/', $restriction);
526 | }
527 | }
528 |
529 | public function groupMembers()
530 | {
531 | global $db;
532 |
533 | $start = request_var('start', 0);
534 | $max = request_var('max', 0);
535 | $return_type = request_var('returnType', ''); // NAME or ENTITY
536 | $restriction = html_entity_decode(request_var('restriction', '', true), ENT_COMPAT, 'UTF-8');
537 |
538 | $searchRestriction = new SearchRestriction($this,
539 | $restriction,
540 | array(
541 | 'name' => 'g.group_name',
542 | ));
543 |
544 | $sql = 'SELECT u.user_id, u.username, u.user_type, u.user_email, u.user_avatar, u.user_avatar_type, u.user_avatar_width, u.user_avatar_width';
545 |
546 | if ($this->config['firstname_column'] && $this->config['lastname_column'])
547 | {
548 | $sql .= ', pf.pf_' . $this->config['firstname_column'] . ' as firstname, pf.pf_' . $this->config['lastname_column'] . ' as lastname';
549 | }
550 |
551 | $sql .= ' FROM (' . GROUPS_TABLE . ' g, ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . ' u)';
552 |
553 | if ($this->config['firstname_column'] && $this->config['lastname_column'])
554 | {
555 | $sql .= ' LEFT JOIN ' . PROFILE_FIELDS_DATA_TABLE . ' pf ON (u.user_id = pf.user_id)';
556 | }
557 |
558 | $sql .= ' WHERE g.group_id = ug.group_id
559 | AND ug.user_id = u.user_id
560 | AND ug.user_pending = 0
561 | AND u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')';
562 | $sql .= $this->users_query('AND', 'u');
563 | $sql .= $this->groups_query('AND', 'g');
564 | $sql .= $searchRestriction->getWhere();
565 | $result = $db->sql_query_limit($sql, $max, $start);
566 |
567 | while ($row = $db->sql_fetchrow($result))
568 | {
569 | $line = ($return_type == 'ENTITY') ? $this->user_row_line($row) : $this->get_user_name($row['username']);
570 |
571 | if ($line !== false)
572 | {
573 | $this->add($line);
574 | }
575 | }
576 | $db->sql_freeresult($result);
577 | }
578 |
579 | public function searchGroups()
580 | {
581 | global $db;
582 |
583 | $start = request_var('start', 0);
584 | $max = request_var('max', 0);
585 | $return_type = request_var('returnType', ''); // NAME or ENTITY
586 | $restriction = html_entity_decode(request_var('restriction', '', true), ENT_COMPAT, 'UTF-8');
587 |
588 | $searchRestriction = new SearchRestriction($this,
589 | $restriction,
590 | array(
591 | 'description' => 'group_desc',
592 | 'name' => 'group_name',
593 | 'active' => "'true'", // all phpBB groups are active, true = true (all), true = false (none)
594 | ));
595 |
596 | $sql = 'SELECT group_id, group_name, group_desc, group_type
597 | FROM ' . GROUPS_TABLE . '
598 | WHERE 1=1';
599 | $sql .= $this->groups_query('AND');
600 | $sql .= $searchRestriction->getWhere();
601 | $result = $db->sql_query_limit($sql, $max, $start);
602 |
603 | while ($row = $db->sql_fetchrow($result))
604 | {
605 | $line = ($return_type == 'ENTITY') ? $this->group_row_line($row) : $this->get_group_name($row['group_name']);
606 |
607 | if ($line !== false)
608 | {
609 | $this->add($line);
610 | }
611 | }
612 | $db->sql_freeresult($result);
613 | }
614 |
615 | public function UserMemberships()
616 | {
617 | global $db;
618 |
619 | $start = request_var('start', 0);
620 | $max = request_var('max', 0);
621 | $return_type = request_var('returnType', ''); // NAME or ENTITY
622 | $restriction = html_entity_decode(request_var('restriction', '', true), ENT_COMPAT, 'UTF-8');
623 |
624 | $searchRestriction = new SearchRestriction($this,
625 | $restriction,
626 | array(
627 | 'name' => 'u.username_clean',
628 | 'groupname' => 'g.group_name',
629 | ));
630 |
631 | $sql = 'SELECT g.group_id, g.group_name, g.group_desc, g.group_type
632 | FROM ' . USERS_TABLE . ' u, ' . USER_GROUP_TABLE . ' ug, ' . GROUPS_TABLE . ' g
633 | WHERE u.user_id = ug.user_id
634 | AND ug.group_id = g.group_id
635 | AND ug.user_pending = 0
636 | AND u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')';
637 | $sql .= $this->users_query('AND', 'u');
638 | $sql .= $this->groups_query('AND', 'g');
639 | $sql .= $searchRestriction->getWhere();
640 |
641 | $result = $db->sql_query_limit($sql, $max, $start);
642 |
643 | while ($row = $db->sql_fetchrow($result))
644 | {
645 | $line = ($return_type == 'ENTITY') ? $this->group_row_line($row) : $this->get_group_name($row['group_name']);
646 |
647 | if ($line !== false)
648 | {
649 | $this->add($line);
650 | }
651 | }
652 | $db->sql_freeresult($result);
653 | }
654 |
655 | // username, user_email, user_active, avatar, first_name, last_name
656 | protected function user_row_line($row)
657 | {
658 | global $config, $phpEx, $phpbb_root_path;
659 |
660 | $icon_location = '';
661 |
662 | if ($this->config['avatar_base_url'])
663 | {
664 | $row['user_avatar'] = html_entity_decode($row['user_avatar'], ENT_COMPAT, 'UTF-8');
665 |
666 | if (!empty($row['user_avatar']) && $row['user_avatar_type'] && $config['allow_avatar'])
667 | {
668 | switch ($row['user_avatar_type'])
669 | {
670 | case AVATAR_UPLOAD:
671 | if ($config['allow_avatar_upload'])
672 | {
673 | $icon_location = $this->config['avatar_base_url'] . "download/file.$phpEx?avatar=" . $row['user_avatar'];
674 | }
675 | break;
676 |
677 | case AVATAR_GALLERY:
678 | if ($config['allow_avatar_local'])
679 | {
680 | $icon_location = $this->config['avatar_base_url'] . $config['avatar_gallery_path'] . '/' . $row['user_avatar'];
681 | }
682 | break;
683 |
684 | case AVATAR_REMOTE:
685 | if ($config['allow_avatar_remote'])
686 | {
687 | $icon_location = $row['user_avatar'];
688 | }
689 | break;
690 | }
691 |
692 | $icon_location = str_replace(' ', '%20', $icon_location);
693 | }
694 | }
695 |
696 | $username = $this->get_user_name($row['username']);
697 |
698 | if ($username === false)
699 | {
700 | return false;
701 | }
702 |
703 | $user_entity_row = array(
704 | 'user_id' => $row['user_id'],
705 | 'username' => $username,
706 | 'user_email' => html_entity_decode($row['user_email'], ENT_COMPAT, 'UTF-8'),
707 | 'user_type' => $row['user_type'],
708 | 'icon_location' => $icon_location,
709 | );
710 |
711 | if (!empty($row['firstname']))
712 | {
713 | $user_entity_row['firstname'] = $row['firstname'];
714 | }
715 |
716 | if (!empty($row['lastname']))
717 | {
718 | $user_entity_row['lastname'] = $row['lastname'];
719 | }
720 |
721 | return implode("\t", $user_entity_row);
722 | }
723 |
724 | protected function group_row_line($row)
725 | {
726 | // Return correct group name
727 | $group_name = $this->get_group_name($row['group_name']);
728 |
729 | if ($group_name === false)
730 | {
731 | return false;
732 | }
733 |
734 | return $row['group_id'] . "\t" . html_entity_decode($group_name, ENT_COMPAT, 'UTF-8') . "\t" . $row['group_type'] . "\t" . html_entity_decode(str_replace("\n", ' ', $row['group_desc']), ENT_COMPAT, 'UTF-8');
735 | }
736 |
737 | /**
738 | * Get correct group name. Prefixed with _api_ to not conflict with phpBB funciton get_group_name()
739 | */
740 | public function get_group_name($group_name, $reverse = false)
741 | {
742 | if ($reverse)
743 | {
744 | return (isset($this->special_groups_reverse[strtolower($group_name)])) ? $this->special_groups_reverse[strtolower($group_name)] : $group_name;
745 | }
746 |
747 | $group_name = (isset($this->special_groups[strtolower($group_name)])) ? $this->special_groups[strtolower($group_name)] : $group_name;
748 | return html_entity_decode($group_name, ENT_COMPAT, 'UTF-8');
749 | }
750 | }
751 |
752 | class SearchRestriction
753 | {
754 | private $obj;
755 | private $columns;
756 | private $api;
757 |
758 | public function __construct($api, $restrictionJson, $columnMap)
759 | {
760 | $this->api = $api;
761 | $this->obj = json_decode($restrictionJson, true);
762 |
763 | $this->columns = $columnMap;
764 | }
765 |
766 | public function propertyToColumn($property)
767 | {
768 | return isset($this->columns[$property]) ? $this->columns[$property] : '';
769 | }
770 |
771 | public function getWhere()
772 | {
773 | $whereClause = $this->recursiveWhere($this->obj);
774 |
775 | if ($whereClause)
776 | {
777 | return ' AND ' . $whereClause;
778 | }
779 |
780 | return '';
781 | }
782 |
783 | protected function recursiveWhere($obj)
784 | {
785 | if (isset($obj['mode'])) // term
786 | {
787 | return $this->whereTerm($obj['mode'], $obj['property'], $obj['value']);
788 | }
789 | else if (isset($obj['boolean'])) // multi term
790 | {
791 | return $this->whereMultiTerm($obj['boolean'], $obj['terms']);
792 | }
793 |
794 | return '';
795 | }
796 |
797 | protected function whereMultiTerm($operator, $operands)
798 | {
799 | $where = '(';
800 |
801 | $first = true;
802 | foreach ($operands as $operand)
803 | {
804 | $whereClause = $this->recursiveWhere($operand);
805 |
806 | if (!empty($whereClause))
807 | {
808 | if (!$first)
809 | {
810 | $where .= ' ' . $operator . ' ';
811 | }
812 | $first = false;
813 |
814 | $where .= $whereClause;
815 | }
816 | }
817 |
818 | $where .= ')';
819 |
820 | return $where;
821 | }
822 |
823 | protected function whereTerm($compareMode, $property, $value)
824 | {
825 | global $db;
826 |
827 | $column = $this->propertyToColumn($property);
828 |
829 | if (empty($column) || (empty($value) && $value !== '0'))
830 | {
831 | return '';
832 | }
833 |
834 | $where = $column . ' ';
835 |
836 | // remove alias to get plain column name
837 | $plain_column = (strpos($column, '.') !== false) ? substr($column, strpos($column, '.') + 1) : $column;
838 |
839 | // Adjust value if we need to search for group name.
840 | if ($plain_column == 'group_name')
841 | {
842 | // Define true as second parameter to reverse the mapping (English name to name stored in database)
843 | $value = $this->api->get_group_name($value, true);
844 | $where = 'LOWER(' . $column . ') ';
845 | $value = strtolower($value);
846 | }
847 |
848 | // Make sure usernames are always "cleaned" up
849 | if ($plain_column == 'username_clean')
850 | {
851 | $value = utf8_clean_string($value);
852 | }
853 |
854 | switch ($compareMode)
855 | {
856 | case 'CONTAINS':
857 | $where .= $db->sql_like_expression($db->get_any_char() . $value . $db->get_any_char());
858 | break;
859 | case 'EXACTLY_MATCHES':
860 | if ($plain_column == 'user_type')
861 | {
862 | if ($value == 'true')
863 | {
864 | $where .= ' <> ';
865 | }
866 | else
867 | {
868 | $where .= ' = ';
869 | }
870 | $where .= USER_INACTIVE;
871 | }
872 | else
873 | {
874 | $where .= '= \'' . $db->sql_escape($value) . '\'';
875 | }
876 | break;
877 | case 'GREATER_THAN':
878 | $where .= '> \'' . (int) $value . '\'';
879 | break;
880 | case 'LESS_THAN':
881 | $where .= '< \'' . (int) $value . '\'';
882 | break;
883 | case 'STARTS_WITH':
884 | $where .= $db->sql_like_expression($value . $db->get_any_char());
885 | break;
886 | }
887 |
888 | return $where;
889 | }
890 | }
891 |
892 | function _api_get_cached_user($cache_path, $api_cache_users)
893 | {
894 | $max = (!empty($_POST['max'])) ? (int) $_POST['max'] : 0;
895 | $start = (!empty($_POST['start'])) ? (int) $_POST['start'] : 0;
896 | $returntype = (!empty($_POST['returnType'])) ? (string) $_POST['returnType'] : '';
897 |
898 | // Check user name...
899 | if ($max == 1 && $start == 0 && $returntype == 'ENTITY')
900 | {
901 | $restriction = (!empty($_POST['restriction'])) ? (string) $_POST['restriction'] : '';
902 | $restriction = json_decode(html_entity_decode((STRIP) ? stripslashes($restriction) : $restriction, ENT_COMPAT, 'UTF-8'), true);
903 |
904 | if ($restriction['mode'] == 'EXACTLY_MATCHES' && $restriction['property'] == 'name' && !empty($restriction['value']))
905 | {
906 | $md5 = md5($restriction['value']);
907 | $first_char = $md5[0];
908 | $second_char = $md5[1];
909 |
910 | if (file_exists($cache_path . $first_char . '/' . $second_char . '/' . $md5))
911 | {
912 | $lastchange = @filemtime($cache_path . $first_char . '/' . $second_char . '/' . $md5);
913 |
914 | if ($lastchange >= time() - $api_cache_users)
915 | {
916 | return file_get_contents($cache_path . $first_char . '/' . $second_char . '/' . $md5);
917 | }
918 | }
919 | }
920 | }
921 |
922 | return false;
923 | }
924 |
925 | function _api_set_cached_user($cache_path, $restriction)
926 | {
927 | $restriction = json_decode($restriction, true);
928 |
929 | if ($restriction['mode'] == 'EXACTLY_MATCHES' && $restriction['property'] == 'name' && !empty($restriction['value']))
930 | {
931 | $md5 = md5($restriction['value']);
932 | $first_char = $md5[0];
933 | $second_char = $md5[1];
934 |
935 | if (!file_exists($cache_path . $first_char))
936 | {
937 | mkdir($cache_path . $first_char);
938 | }
939 |
940 | if (!file_exists($cache_path . $first_char . '/' . $second_char))
941 | {
942 | mkdir($cache_path . $first_char . '/' . $second_char);
943 | }
944 |
945 | $fp = fopen($cache_path . $first_char . '/' . $second_char . '/' . $md5, 'w');
946 | fwrite($fp, $line . "\n");
947 | fclose($fp);
948 | }
949 | }
950 |
--------------------------------------------------------------------------------