001/*
002 *  Copyright 2017 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.core.authentication.token;
017
018import java.io.IOException;
019import java.io.InputStream;
020import java.io.UnsupportedEncodingException;
021import java.sql.Connection;
022import java.sql.PreparedStatement;
023import java.sql.ResultSet;
024import java.sql.SQLException;
025import java.sql.Timestamp;
026import java.sql.Types;
027import java.util.ArrayList;
028import java.util.Base64;
029import java.util.Date;
030import java.util.List;
031import java.util.Map;
032
033import org.apache.avalon.framework.activity.Initializable;
034import org.apache.avalon.framework.component.Component;
035import org.apache.avalon.framework.service.ServiceException;
036import org.apache.avalon.framework.service.ServiceManager;
037import org.apache.avalon.framework.service.Serviceable;
038import org.apache.commons.codec.digest.DigestUtils;
039import org.apache.commons.io.IOUtils;
040import org.apache.commons.lang.RandomStringUtils;
041import org.apache.commons.lang3.StringUtils;
042
043import org.ametys.core.datasource.ConnectionHelper;
044import org.ametys.core.datasource.dbtype.SQLDatabaseTypeExtensionPoint;
045import org.ametys.core.ui.Callable;
046import org.ametys.core.user.CurrentUserProvider;
047import org.ametys.core.user.UserIdentity;
048import org.ametys.runtime.config.Config;
049import org.ametys.runtime.plugin.component.AbstractLogEnabled;
050
051/**
052 * The component to handle temporary authentication token.<br>
053 * Token can only be used once and are available for a short time only.
054 */
055public class AuthenticationTokenManager extends AbstractLogEnabled implements Component, Serviceable, Initializable
056{
057    /** The avalon role */
058    public static final String ROLE = AuthenticationTokenManager.class.getName();
059
060    /** The separator in token */
061    public static final String TOKEN_SEPARATOR = "#";
062
063    /** The user token type */
064    public static final String USER_TOKEN_TYPE = "User";
065    
066    /** all fields without login and population_id */
067    private static final String TOKEN_SQL_GET_FIELDS = "id, token, salt, creation_date, end_date, last_update_date, nb_uses_left, auto_renew_duration, context, type, token_comment";
068    
069    /** all fields without id and last_update_date */
070    private static final String TOKEN_SQL_SET_FIELDS = "login, population_id, token, salt, creation_date, end_date, nb_uses_left, auto_renew_duration, context, type, token_comment";
071
072    private ServiceManager _manager;
073
074    private CurrentUserProvider _currentUserProvider;
075
076    private String _datasourceId;
077    
078    private SQLDatabaseTypeExtensionPoint _sqlDatabaseTypeExtensionPoint;
079
080    public void service(ServiceManager manager) throws ServiceException
081    {
082        _manager = manager;
083        _sqlDatabaseTypeExtensionPoint = (SQLDatabaseTypeExtensionPoint) manager.lookup(SQLDatabaseTypeExtensionPoint.ROLE);
084    }
085
086    public void initialize() throws Exception
087    {
088        _datasourceId = Config.getInstance() != null ? Config.getInstance().getValue("runtime.assignments.authenticationtokens") : "";
089    }
090
091    private CurrentUserProvider _getCurrentUserProvider() throws RuntimeException
092    {
093        if (_currentUserProvider == null)
094        {
095            try
096            {
097                _currentUserProvider = (CurrentUserProvider) _manager.lookup(CurrentUserProvider.ROLE);
098            }
099            catch (ServiceException e)
100            {
101                throw new RuntimeException(e);
102            }
103        }
104        return _currentUserProvider;
105    }
106
107    /**
108     * Get the existing tokens for the connected user
109     * 
110     * @param type The type of tokens to return. null to return all.
111     * @return The tokens
112     * @throws RuntimeException If there is no user connected or if there is a
113     *             database error
114     */
115    public List<Token> getTokens(String type) throws RuntimeException
116    {
117        return getTokens(_getCurrentUserProvider().getUser(), type);
118    }
119
120    /**
121     * Get the existing tokens for this user
122     * 
123     * @param type The type of tokens to return. null to return all.
124     * @param user The user. Cannot be null
125     * @return The tokens identifier and associated comment
126     * @throws RuntimeException If the user is null or if there is a database
127     *             error
128     */
129    public List<Token> getTokens(UserIdentity user, String type) throws RuntimeException
130    {
131        if (user == null)
132        {
133            throw new RuntimeException("Cannot generate a temporary authentication token for a null user");
134        }
135
136        List<Token> tokens = new ArrayList<>();
137
138        Connection connection = null;
139        try
140        {
141            connection = ConnectionHelper.getConnection(_datasourceId);
142
143            // Delete old entries
144            _deleteOldTokens(connection);
145
146            try (PreparedStatement selectStatement = _getSelectUserTokenStatement(connection, user.getLogin(), user.getPopulationId(), type);
147                    ResultSet resultSet = selectStatement.executeQuery())
148            {
149                // Find the database entry using this token
150                while (resultSet.next())
151                {
152                    Token token = _getTokenFromResultSet(resultSet, connection);
153                    tokens.add(token);
154                }
155            }
156        }
157        catch (Exception e)
158        {
159            getLogger().error("Communication error with the database", e);
160        }
161        finally
162        {
163            ConnectionHelper.cleanup(connection);
164        }
165
166        return tokens;
167    }
168    
169    /**
170     * Generates a new token for the current user
171     * 
172     * @param duration The time the token is valid in seconds. 0 means for ever
173     *            and moreover the ticket will be reusable.
174     * @param type The type of token. Mandatory but can be anything you want
175     *            between 1 to 32 characters. Such as "Cookie".
176     * @param comment An optional token comment to remember the reason of its
177     *            creation
178     * @return The token
179     * @throws RuntimeException If the user is not authenticated, or if there is
180     *             a database error
181     */
182    public String generateToken(long duration, String type, String comment) throws RuntimeException
183    {
184        return generateToken(_getCurrentUserProvider().getUser(), duration, type, comment);
185    }
186
187    /**
188     * Generates a new token
189     * 
190     * @param user The user that will be authenticated with the token
191     * @param duration The time the token is valid in seconds. 0 means for ever
192     *            and moreover the ticket will be reusable
193     * @param type The type of token. Mandatory but can be anything you want
194     *            between 1 to 32 characters. Such as "Cookie".
195     * @param comment An optional token comment to remember the reason of its
196     *            creation
197     * @return The token
198     * @throws RuntimeException If the user is null or if there is a database
199     *             error or if duration is negative
200     */
201    public String generateToken(UserIdentity user, long duration, String type, String comment) throws RuntimeException
202    {
203        return generateToken(user, duration, false, null, null, type, comment);
204    }
205    /**
206     * Generates a new token
207     * 
208     * @param user The user that will be authenticated with the token
209     * @param duration The time the token is valid in seconds. 0 means for ever
210     *            and moreover the ticket will be reusable
211     * @param nbUsesLeft number of available uses (null for no limit)
212     * @param type The type of token. Mandatory but can be anything you want
213     *            between 1 to 32 characters. Such as "Cookie".
214     * @param comment An optional token comment to remember the reason of its
215     *            creation
216     * @return The token
217     * @throws RuntimeException If the user is null or if there is a database
218     *             error or if duration is negative
219     */
220    public String generateToken(UserIdentity user, long duration, Integer nbUsesLeft, String type, String comment) throws RuntimeException
221    {
222        return generateToken(user, duration, false, nbUsesLeft, null, type, comment);
223    }
224    
225    /**
226     * Generates a new token
227     * 
228     * @param user The user that will be authenticated with the token
229     * @param duration The time the token is valid in seconds. 0 means for ever
230     *            and moreover the ticket will be reusable
231     * @param autoRenewDuration true to automatically renew token if used before it's expiration
232     * @param nbUsesLeft number of available uses (null for no limit)
233     * @param context context where the token can be used
234     * @param type The type of token. Mandatory but can be anything you want
235     *            between 1 to 32 characters. Such as "Cookie".
236     * @param comment An optional token comment to remember the reason of its
237     *            creation
238     * @return The token
239     * @throws RuntimeException If the user is null or if there is a database
240     *             error or if duration is negative
241     */
242    public String generateToken(UserIdentity user, long duration, boolean autoRenewDuration, Integer nbUsesLeft, String context, String type, String comment) throws RuntimeException
243    {
244        if (user == null)
245        {
246            throw new RuntimeException("Cannot generate a temporary authentication token for a null user");
247        }
248        else if (duration < 0)
249        {
250            throw new RuntimeException("Cannot generate a token for a negative duration [" + duration + "]");
251        }
252
253        String token = RandomStringUtils.randomAlphanumeric(duration == 0 ? 64 : 16);
254        String salt = RandomStringUtils.randomAlphanumeric(48);
255        Timestamp creationDateTime = new Timestamp(new Date().getTime());
256        Timestamp endTime = duration > 0 ? new Timestamp(System.currentTimeMillis() + duration * 1000) : null;
257        String hashedTokenAndSalt = DigestUtils.sha512Hex(token + salt);
258
259        
260        _generateToken(user, duration, autoRenewDuration, nbUsesLeft, context, type, comment, hashedTokenAndSalt, salt, creationDateTime, endTime);
261
262        String fullToken = user.getPopulationId() + TOKEN_SEPARATOR + user.getLogin() + TOKEN_SEPARATOR + token;
263        
264        try
265        {
266            return Base64.getEncoder().withoutPadding().encodeToString(fullToken.getBytes("UTF-8"));
267        }
268        catch (UnsupportedEncodingException e)
269        {
270            // can't occur ...
271            throw new RuntimeException(e);
272        }
273    }
274    
275    private void _generateToken(UserIdentity user, long duration, boolean autoRenewDuration, Integer nbUsesLeft, String context, String type, String comment, String hashedTokenAndSalt, String salt, Timestamp creationDateTime, Timestamp endTime) throws RuntimeException
276    {
277        ResultSet rs = null;
278        Connection connection = null;
279        PreparedStatement statement = null;
280        try
281        {
282            connection = ConnectionHelper.getConnection(_datasourceId);
283            String dbType = ConnectionHelper.getDatabaseType(connection);
284            if (ConnectionHelper.DATABASE_ORACLE.equals(dbType))
285            {
286                statement = connection.prepareStatement("SELECT seq_authenticationtoken.nextval FROM dual");
287                rs = statement.executeQuery();
288    
289                String id = null;
290                if (rs.next())
291                {
292                    id = rs.getString(1);
293                }
294                ConnectionHelper.cleanup(rs);
295                ConnectionHelper.cleanup(statement);
296                statement = connection.prepareStatement(
297                        "INSERT INTO Authentication_Token (id, " + TOKEN_SQL_SET_FIELDS + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
298                int nextParam = 1;
299                statement.setString(nextParam++, id);
300                statement.setString(nextParam++, user.getLogin());
301                statement.setString(nextParam++, user.getPopulationId());
302                statement.setString(nextParam++, hashedTokenAndSalt);
303                statement.setString(nextParam++, salt);
304                statement.setTimestamp(nextParam++, creationDateTime);
305                statement.setTimestamp(nextParam++, endTime);
306                if (nbUsesLeft == null)
307                {
308                    statement.setNull(nextParam++, java.sql.Types.INTEGER);
309                }
310                else
311                {
312                    statement.setInt(nextParam++, nbUsesLeft);
313                }
314    
315                if (!autoRenewDuration)
316                {
317                    statement.setNull(nextParam++, java.sql.Types.BIGINT);
318                }
319                else
320                {
321                    statement.setLong(nextParam++, duration);
322                }
323                
324                if (context == null)
325                {
326                    statement.setNull(nextParam++, java.sql.Types.VARCHAR);
327                }
328                else
329                {
330                    statement.setString(nextParam++, context);
331                }
332                statement.setString(nextParam++, type);
333                if (comment == null)
334                {
335                    statement.setNull(nextParam++, Types.BLOB);
336                }
337                else
338                {
339                    _sqlDatabaseTypeExtensionPoint.setBlob(dbType, statement, nextParam++, comment);
340                }
341            }
342            else
343            {
344                statement = connection.prepareStatement(
345                        "INSERT INTO Authentication_Token (" + TOKEN_SQL_SET_FIELDS + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
346                int nextParam = 1;
347                statement.setString(nextParam++, user.getLogin());
348                statement.setString(nextParam++, user.getPopulationId());
349                statement.setString(nextParam++, hashedTokenAndSalt);
350                statement.setString(nextParam++, salt);
351                statement.setTimestamp(nextParam++, creationDateTime);
352                statement.setTimestamp(nextParam++, endTime);
353                
354                if (nbUsesLeft == null)
355                {
356                    statement.setNull(nextParam++, java.sql.Types.INTEGER);
357                }
358                else
359                {
360                    statement.setInt(nextParam++, nbUsesLeft);
361                }
362    
363                if (!autoRenewDuration)
364                {
365                    statement.setNull(nextParam++, java.sql.Types.BIGINT);
366                }
367                else
368                {
369                    statement.setLong(nextParam++, duration);
370                }
371                
372                if (context == null)
373                {
374                    statement.setNull(nextParam++, java.sql.Types.VARCHAR);
375                }
376                else
377                {
378                    statement.setString(nextParam++, context);
379                }
380                
381                statement.setString(nextParam++, type);
382                _sqlDatabaseTypeExtensionPoint.setBlob(dbType, statement, nextParam++, comment);
383            }
384            
385            statement.executeUpdate();
386        }
387        catch (SQLException | UnsupportedEncodingException e)
388        {
389            throw new RuntimeException(e);
390        }
391        finally
392        {
393            ConnectionHelper.cleanup(rs);
394            ConnectionHelper.cleanup(connection);
395        }
396    }
397
398    private UserIdentity _validateToken(String encodedToken, String context, boolean forceRemove)
399    {
400        String token;
401        try
402        {
403            token = new String(Base64.getDecoder().decode(encodedToken), "UTF-8");
404        }
405        catch (UnsupportedEncodingException e)
406        {
407            // can't occur ...
408            throw new RuntimeException(e);
409        }
410        catch (Exception e)
411        {
412            // Exception occured during token decoding
413            return null;
414        }
415        
416        String[] split = StringUtils.split(token, TOKEN_SEPARATOR);
417        if (split == null || split.length != 3)
418        {
419            return null;
420        }
421
422        String populationId = split[0];
423        String login = split[1];
424        String tokenPart = split[2];
425
426        Connection connection = null;
427        try
428        {
429            connection = ConnectionHelper.getConnection(_datasourceId);
430
431            // Delete old entries
432            _deleteOldTokens(connection);
433
434            try (
435                    PreparedStatement selectStatement = _getSelectUserTokenStatement(connection, login, populationId, null);
436                    ResultSet resultSet = selectStatement.executeQuery()
437                )
438            {
439                // Find the database entry using this token
440                while (resultSet.next())
441                {
442                    if (resultSet.getString("token").equals(DigestUtils.sha512Hex(tokenPart + resultSet.getString("salt"))))
443                    {
444                        Token tokenToUpdate = _getTokenFromResultSet(resultSet, connection);
445                        // If the token doesn't have a context, this one is always valid
446                        // Else, the token context must be equal to the token context the context method parameter
447                        if (tokenToUpdate.getContext() != null && !tokenToUpdate.getContext().equals(context))
448                        {
449                            continue;
450                        }
451                        
452                        // Delete it
453                        if (forceRemove)
454                        {
455                            _deleteUserToken(connection, tokenToUpdate.getId());
456                        }
457                        else
458                        {
459                            _updateUserToken(connection, tokenToUpdate);
460                        }
461                        return new UserIdentity(login, populationId);
462                    }
463                }
464            }
465        }
466        catch (Exception e)
467        {
468            getLogger().error("Communication error with the database", e);
469        }
470        finally
471        {
472            ConnectionHelper.cleanup(connection);
473        }
474
475        return null;
476    }
477
478    /**
479     * Check if a token is valid and return the user
480     * 
481     * @param token The token to validate
482     * @return The user associated to the valid token, null otherwise
483     */
484    public UserIdentity validateToken(String token)
485    {
486        return validateToken(token, null);
487    }
488    
489    /**
490     * Check if a token is valid and return the user
491     * @param token The token to validate
492     * @param context context to validate the token with
493     * @return The user associated to the valid token, null otherwise
494     */
495    public UserIdentity validateToken(String token, String context)
496    {
497        return _validateToken(token, context, false);
498    }
499
500    /**
501     * Destroy the given token
502     * 
503     * @param token The token to remove
504     * @param context context of the token (null for no context)
505     */
506    public void deleteTokenByValue(String token, String context)
507    {
508        _validateToken(token, context, true);
509    }
510
511    /**
512     * Destroy the given token
513     * 
514     * @param tokenId The token identifier to remove
515     */
516    public void deleteTokenById(Integer tokenId)
517    {
518        Connection connection = null;
519        try
520        {
521            connection = ConnectionHelper.getConnection(_datasourceId);
522
523            _deleteUserToken(connection, tokenId);
524        }
525        catch (SQLException e)
526        {
527            throw new RuntimeException("Could not delete the authentication token with identifier " + tokenId, e);
528        }
529        finally
530        {
531            ConnectionHelper.cleanup(connection);
532        }
533    }
534
535    /**
536     * Generates the sql statement that deletes the entries of the users token
537     * database that are old
538     * 
539     * @param connection the database's session
540     * @throws SQLException if a sql exception occurs
541     */
542    private void _deleteOldTokens(Connection connection) throws SQLException
543    {
544        try (PreparedStatement statement = connection.prepareStatement("DELETE FROM Authentication_Token WHERE end_date < ? OR nb_uses_left = ?"))
545        {
546            statement.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
547            statement.setInt(2, 0);
548            statement.executeUpdate();
549        }
550    }
551
552    /**
553     * Generates the statement that selects the users having the specified login
554     * in the Authentication_Token table
555     * 
556     * @param connection the database's session
557     * @param login The login of the user
558     * @param populationId The populationId of the user
559     * @param type The type to filter or null to get all
560     * @return the retrieve statement
561     * @throws SQLException if a sql exception occurs
562     */
563    private PreparedStatement _getSelectUserTokenStatement(Connection connection, String login, String populationId, String type) throws SQLException
564    {
565        String sqlRequest = "SELECT " + TOKEN_SQL_GET_FIELDS + " FROM Authentication_Token WHERE login=? AND population_id=?"
566                + (type != null ? " AND type=?" : "");
567
568        PreparedStatement statement = connection.prepareStatement(sqlRequest);
569        int nextParam = 1;
570        statement.setString(nextParam++, login);
571        statement.setString(nextParam++, populationId);
572        if (type != null)
573        {
574            statement.setString(nextParam++, type);
575        }
576
577        return statement;
578    }
579    private Token _getTokenFromResultSet(ResultSet resultSet, Connection connection) throws SQLException, IOException
580    {
581        String dbType = ConnectionHelper.getDatabaseType(connection);
582        String comment = null;
583        try (InputStream blob = _sqlDatabaseTypeExtensionPoint.getBlob(dbType, resultSet, "token_comment"))
584        {
585            if (blob != null)
586            {
587                comment = IOUtils.toString(blob, "UTF-8");
588            }
589        }
590        
591        Integer nbUsesLeft = resultSet.getInt("nb_uses_left");
592        if (resultSet.wasNull())
593        {
594            nbUsesLeft = null;
595        }
596        
597        Long autoRenewDuration = resultSet.getLong("auto_renew_duration");
598        if (resultSet.wasNull())
599        {
600            autoRenewDuration = null;
601        }
602        Token token = new Token(resultSet.getInt("id"), resultSet.getString("type"), comment, resultSet.getTimestamp("creation_date"),
603                resultSet.getTimestamp("end_date"), resultSet.getTimestamp("last_update_date"), nbUsesLeft, autoRenewDuration, resultSet.getString("context"));
604        return token;
605    }
606
607    /**
608     * Deletes the database entry that has this token
609     * 
610     * @param connection the database's session
611     * @param id the token id
612     * @throws SQLException if an error occurred
613     */
614    private void _deleteUserToken(Connection connection, Integer id) throws SQLException
615    {
616        try (PreparedStatement statement = connection.prepareStatement("DELETE FROM Authentication_Token WHERE id = ?"))
617        {
618            statement.setInt(1, id);
619            statement.executeUpdate();
620        }
621    }
622
623    /**
624     * Update the last update date in the database
625     * 
626     * @param connection the database's session
627     * @param token the token
628     * @throws SQLException if an error occurred
629     */
630    private void _updateUserToken(Connection connection, Token token) throws SQLException
631    {
632        Integer nbUsesLeft = token.getNbUsesLeft();
633        if (nbUsesLeft != null && nbUsesLeft > 0)
634        {
635            nbUsesLeft--;
636        }
637
638        // Delete if no uses left
639        if (nbUsesLeft != null && nbUsesLeft == 0)
640        {
641            _deleteUserToken(connection, token.getId());
642        }
643        // Update last update_date and nb_use_left if needed
644        // And update end_date if the token have auto renew
645        else if (token.getAutoRenewDuration() != null)
646        {
647            Long autoRenewDuration = token.getAutoRenewDuration();
648            Timestamp endDate = new Timestamp(new Date().getTime() + autoRenewDuration * 1000);
649
650            try (PreparedStatement statement = connection.prepareStatement("UPDATE Authentication_Token SET last_update_date = ?, end_date = ?, nb_uses_left = ? WHERE id = ?"))
651            {
652                Timestamp lastUpdateDate = new Timestamp(new Date().getTime());
653                statement.setTimestamp(1, lastUpdateDate);
654                statement.setTimestamp(2, endDate);
655                if (nbUsesLeft == null)
656                {
657                    statement.setNull(3, java.sql.Types.INTEGER);
658                }
659                else
660                {
661                    statement.setInt(3, nbUsesLeft);
662                }
663                statement.setInt(4, token.getId());
664                statement.executeUpdate();
665            }
666        }
667        else
668        {
669            try (PreparedStatement statement = connection.prepareStatement("UPDATE Authentication_Token SET last_update_date = ?, nb_uses_left = ? WHERE id = ?"))
670            {
671                Timestamp lastUpdateDate = new Timestamp(new Date().getTime());
672                statement.setTimestamp(1, lastUpdateDate);
673                if (nbUsesLeft == null)
674                {
675                    statement.setNull(2, java.sql.Types.INTEGER);
676                }
677                else
678                {
679                    statement.setInt(2, nbUsesLeft);
680                }
681                statement.setInt(3, token.getId());
682                statement.executeUpdate();
683            }
684        }
685    }
686
687    /**
688     * Generate a new authentication token
689     * 
690     * @param parameters a map of the following parameters for the
691     *            authentication token : description
692     * @return The generated token
693     */
694    @Callable
695    public String generateAuthenticationToken(Map<String, Object> parameters)
696    {
697        String description = (String) parameters.get("description");
698        String generateToken = generateToken(0, USER_TOKEN_TYPE, description);
699
700        return generateToken;
701    }
702
703    /**
704     * Delete one or multiples authentication token
705     * 
706     * @param ids a list of authentication token ids
707     */
708    @Callable
709    public void deleteAuthenticationToken(List<Integer> ids)
710    {
711        for (Integer tokenId : ids)
712        {
713            deleteTokenById(tokenId);
714        }
715    }
716
717    /**
718     * An Ametys authentication token
719     */
720    public static class Token
721    {
722        /** The token identifier */
723        protected Integer _id;
724
725        /** The token type */
726        protected String _type;
727
728        /** The token associated comment */
729        protected String _comment;
730
731        /** The token creation date */
732        protected Date _creationDate;
733
734        /** The token end date */
735        protected Date _endDate;
736
737        /** The token last update date */
738        protected Date _lastUpdateDate;
739        
740        /** The token number of use left */
741        protected Integer _nbUsesLeft;
742        
743        /** The token auto renewal duration, in seconds */
744        protected Long _autoRenewDuration;
745        
746        /** The token context */
747        protected String _context;
748
749        /**
750         * Creates a Token
751         * 
752         * @param id The identifier
753         * @param type The type of token
754         * @param comment The comment. Can be null.
755         * @param creationDate The creation date. Can be null.
756         * @param endDate The end date. Can be null.
757         * @param lastUpdateDate The last update date. Can be null.
758         * @param nbUsesLeft nb of uses left for this token (null for no limitation)
759         * @param autoRenewDuration duration that will be added (from "now") each time the token is updated
760         * @param context context of the token (ressource id for example)
761         */
762        protected Token(Integer id, String type, String comment, Date creationDate, Date endDate, Date lastUpdateDate, Integer nbUsesLeft, Long autoRenewDuration, String context)
763        {
764            _id = id;
765            _type = type;
766            _comment = comment;
767            _creationDate = creationDate;
768            _endDate = endDate;
769            _lastUpdateDate = lastUpdateDate;
770            _nbUsesLeft = nbUsesLeft;
771            _autoRenewDuration = autoRenewDuration;
772            _context = context;
773        }
774
775        /**
776         * Get the token identifier
777         * 
778         * @return The identifier
779         */
780        public Integer getId()
781        {
782            return _id;
783        }
784
785        /**
786         * Get the token type
787         * 
788         * @return The type
789         */
790        public String getType()
791        {
792            return _type;
793        }
794
795        /**
796         * Get the associated creation comment
797         * 
798         * @return The comment or null
799         */
800        public String getComment()
801        {
802            return _comment;
803        }
804
805        /**
806         * Get the creation date of the token
807         * 
808         * @return The creation date or null
809         */
810        public Date getCreationDate()
811        {
812            return _creationDate;
813        }
814
815        /**
816         * Get the end date of the token validity
817         * 
818         * @return The end date or null
819         */
820        public Date getEndDate()
821        {
822            return _endDate;
823        }
824
825        /**
826         * Get the last update date of the token
827         * 
828         * @return The last update date or null
829         */
830        public Date getLastUpdateDate()
831        {
832            return _endDate;
833        }
834
835        /**
836         * Get the duration which the token should be automatically renewed on each use, in seconds
837         * 
838         * @return The auto renewal duration in seconds or null if not applicable
839         */
840        public Long getAutoRenewDuration()
841        {
842            return _autoRenewDuration;
843        }
844
845        /**
846         * Get the context on which the token can be used
847         * 
848         * @return The token context or null if not applicable
849         */
850        public String getContext()
851        {
852            return _context;
853        }
854
855        /**
856         * Get the number of uses allowed for this token
857         * 
858         * @return The number of allowed uses or null if not applicable
859         */
860        public Integer getNbUsesLeft()
861        {
862            return _nbUsesLeft;
863        }
864        
865        
866    }
867}