1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.engine; |
7 | |
8 | import java.util.ArrayList; |
9 | import java.util.Arrays; |
10 | |
11 | import org.h2.api.ErrorCode; |
12 | import org.h2.message.DbException; |
13 | import org.h2.message.Trace; |
14 | import org.h2.schema.Schema; |
15 | import org.h2.security.SHA256; |
16 | import org.h2.table.MetaTable; |
17 | import org.h2.table.RangeTable; |
18 | import org.h2.table.Table; |
19 | import org.h2.table.TableView; |
20 | import org.h2.util.MathUtils; |
21 | import org.h2.util.New; |
22 | import org.h2.util.StringUtils; |
23 | import org.h2.util.Utils; |
24 | |
25 | /** |
26 | * Represents a user object. |
27 | */ |
28 | public class User extends RightOwner { |
29 | |
30 | private final boolean systemUser; |
31 | private byte[] salt; |
32 | private byte[] passwordHash; |
33 | private boolean admin; |
34 | |
35 | public User(Database database, int id, String userName, boolean systemUser) { |
36 | super(database, id, userName, Trace.USER); |
37 | this.systemUser = systemUser; |
38 | } |
39 | |
40 | public void setAdmin(boolean admin) { |
41 | this.admin = admin; |
42 | } |
43 | |
44 | public boolean isAdmin() { |
45 | return admin; |
46 | } |
47 | |
48 | /** |
49 | * Set the salt and hash of the password for this user. |
50 | * |
51 | * @param salt the salt |
52 | * @param hash the password hash |
53 | */ |
54 | public void setSaltAndHash(byte[] salt, byte[] hash) { |
55 | this.salt = salt; |
56 | this.passwordHash = hash; |
57 | } |
58 | |
59 | /** |
60 | * Set the user name password hash. A random salt is generated as well. |
61 | * The parameter is filled with zeros after use. |
62 | * |
63 | * @param userPasswordHash the user name password hash |
64 | */ |
65 | public void setUserPasswordHash(byte[] userPasswordHash) { |
66 | if (userPasswordHash != null) { |
67 | if (userPasswordHash.length == 0) { |
68 | salt = passwordHash = userPasswordHash; |
69 | } else { |
70 | salt = new byte[Constants.SALT_LEN]; |
71 | MathUtils.randomBytes(salt); |
72 | passwordHash = SHA256.getHashWithSalt(userPasswordHash, salt); |
73 | } |
74 | } |
75 | } |
76 | |
77 | @Override |
78 | public String getCreateSQLForCopy(Table table, String quotedName) { |
79 | throw DbException.throwInternalError(); |
80 | } |
81 | |
82 | @Override |
83 | public String getCreateSQL() { |
84 | return getCreateSQL(true); |
85 | } |
86 | |
87 | @Override |
88 | public String getDropSQL() { |
89 | return null; |
90 | } |
91 | |
92 | /** |
93 | * Checks that this user has the given rights for this database object. |
94 | * |
95 | * @param table the database object |
96 | * @param rightMask the rights required |
97 | * @throws DbException if this user does not have the required rights |
98 | */ |
99 | public void checkRight(Table table, int rightMask) { |
100 | if (!hasRight(table, rightMask)) { |
101 | throw DbException.get(ErrorCode.NOT_ENOUGH_RIGHTS_FOR_1, table.getSQL()); |
102 | } |
103 | } |
104 | |
105 | /** |
106 | * See if this user has the given rights for this database object. |
107 | * |
108 | * @param table the database object, or null for schema-only check |
109 | * @param rightMask the rights required |
110 | * @return true if the user has the rights |
111 | */ |
112 | public boolean hasRight(Table table, int rightMask) { |
113 | if (rightMask != Right.SELECT && !systemUser && table != null) { |
114 | table.checkWritingAllowed(); |
115 | } |
116 | if (admin) { |
117 | return true; |
118 | } |
119 | Role publicRole = database.getPublicRole(); |
120 | if (publicRole.isRightGrantedRecursive(table, rightMask)) { |
121 | return true; |
122 | } |
123 | if (table instanceof MetaTable || table instanceof RangeTable) { |
124 | // everybody has access to the metadata information |
125 | return true; |
126 | } |
127 | if (table != null) { |
128 | if (hasRight(null, Right.ALTER_ANY_SCHEMA)) { |
129 | return true; |
130 | } |
131 | String tableType = table.getTableType(); |
132 | if (Table.VIEW.equals(tableType)) { |
133 | TableView v = (TableView) table; |
134 | if (v.getOwner() == this) { |
135 | // the owner of a view has access: |
136 | // SELECT * FROM (SELECT * FROM ...) |
137 | return true; |
138 | } |
139 | } else if (tableType == null) { |
140 | // function table |
141 | return true; |
142 | } |
143 | if (table.isTemporary() && !table.isGlobalTemporary()) { |
144 | // the owner has all rights on local temporary tables |
145 | return true; |
146 | } |
147 | } |
148 | if (isRightGrantedRecursive(table, rightMask)) { |
149 | return true; |
150 | } |
151 | return false; |
152 | } |
153 | |
154 | /** |
155 | * Get the CREATE SQL statement for this object. |
156 | * |
157 | * @param password true if the password (actually the salt and hash) should |
158 | * be returned |
159 | * @return the SQL statement |
160 | */ |
161 | public String getCreateSQL(boolean password) { |
162 | StringBuilder buff = new StringBuilder("CREATE USER IF NOT EXISTS "); |
163 | buff.append(getSQL()); |
164 | if (comment != null) { |
165 | buff.append(" COMMENT ").append(StringUtils.quoteStringSQL(comment)); |
166 | } |
167 | if (password) { |
168 | buff.append(" SALT '"). |
169 | append(StringUtils.convertBytesToHex(salt)). |
170 | append("' HASH '"). |
171 | append(StringUtils.convertBytesToHex(passwordHash)). |
172 | append('\''); |
173 | } else { |
174 | buff.append(" PASSWORD ''"); |
175 | } |
176 | if (admin) { |
177 | buff.append(" ADMIN"); |
178 | } |
179 | return buff.toString(); |
180 | } |
181 | |
182 | /** |
183 | * Check the password of this user. |
184 | * |
185 | * @param userPasswordHash the password data (the user password hash) |
186 | * @return true if the user password hash is correct |
187 | */ |
188 | boolean validateUserPasswordHash(byte[] userPasswordHash) { |
189 | if (userPasswordHash.length == 0 && passwordHash.length == 0) { |
190 | return true; |
191 | } |
192 | if (userPasswordHash.length == 0) { |
193 | userPasswordHash = SHA256.getKeyPasswordHash(getName(), new char[0]); |
194 | } |
195 | byte[] hash = SHA256.getHashWithSalt(userPasswordHash, salt); |
196 | return Utils.compareSecure(hash, passwordHash); |
197 | } |
198 | |
199 | /** |
200 | * Check if this user has admin rights. An exception is thrown if he does |
201 | * not have them. |
202 | * |
203 | * @throws DbException if this user is not an admin |
204 | */ |
205 | public void checkAdmin() { |
206 | if (!admin) { |
207 | throw DbException.get(ErrorCode.ADMIN_RIGHTS_REQUIRED); |
208 | } |
209 | } |
210 | |
211 | /** |
212 | * Check if this user has schema admin rights. An exception is thrown if he |
213 | * does not have them. |
214 | * |
215 | * @throws DbException if this user is not a schema admin |
216 | */ |
217 | public void checkSchemaAdmin() { |
218 | if (!hasRight(null, Right.ALTER_ANY_SCHEMA)) { |
219 | throw DbException.get(ErrorCode.ADMIN_RIGHTS_REQUIRED); |
220 | } |
221 | } |
222 | |
223 | @Override |
224 | public int getType() { |
225 | return DbObject.USER; |
226 | } |
227 | |
228 | @Override |
229 | public ArrayList<DbObject> getChildren() { |
230 | ArrayList<DbObject> children = New.arrayList(); |
231 | for (Right right : database.getAllRights()) { |
232 | if (right.getGrantee() == this) { |
233 | children.add(right); |
234 | } |
235 | } |
236 | for (Schema schema : database.getAllSchemas()) { |
237 | if (schema.getOwner() == this) { |
238 | children.add(schema); |
239 | } |
240 | } |
241 | return children; |
242 | } |
243 | |
244 | @Override |
245 | public void removeChildrenAndResources(Session session) { |
246 | for (Right right : database.getAllRights()) { |
247 | if (right.getGrantee() == this) { |
248 | database.removeDatabaseObject(session, right); |
249 | } |
250 | } |
251 | database.removeMeta(session, getId()); |
252 | salt = null; |
253 | Arrays.fill(passwordHash, (byte) 0); |
254 | passwordHash = null; |
255 | invalidate(); |
256 | } |
257 | |
258 | @Override |
259 | public void checkRename() { |
260 | // ok |
261 | } |
262 | |
263 | /** |
264 | * Check that this user does not own any schema. An exception is thrown if |
265 | * he owns one or more schemas. |
266 | * |
267 | * @throws DbException if this user owns a schema |
268 | */ |
269 | public void checkOwnsNoSchemas() { |
270 | for (Schema s : database.getAllSchemas()) { |
271 | if (this == s.getOwner()) { |
272 | throw DbException.get(ErrorCode.CANNOT_DROP_2, getName(), s.getName()); |
273 | } |
274 | } |
275 | } |
276 | |
277 | } |