< prev index next >

src/java.base/share/classes/javax/security/auth/Subject.java

Print this page




 137      * with an empty {@code Set} of Principals and empty
 138      * Sets of public and private credentials.
 139      *
 140      * <p> The newly constructed Sets check whether this {@code Subject}
 141      * has been set read-only before permitting subsequent modifications.
 142      * The newly created Sets also prevent illegal modifications
 143      * by ensuring that callers have sufficient permissions.  These Sets
 144      * also prohibit null elements, and attempts to add or query a null
 145      * element will result in a {@code NullPointerException}.
 146      *
 147      * <p> To modify the Principals Set, the caller must have
 148      * {@code AuthPermission("modifyPrincipals")}.
 149      * To modify the public credential Set, the caller must have
 150      * {@code AuthPermission("modifyPublicCredentials")}.
 151      * To modify the private credential Set, the caller must have
 152      * {@code AuthPermission("modifyPrivateCredentials")}.
 153      */
 154     public Subject() {
 155 
 156         this.principals = Collections.synchronizedSet
 157                         (new SecureSet<Principal>(this, PRINCIPAL_SET));
 158         this.pubCredentials = Collections.synchronizedSet
 159                         (new SecureSet<Object>(this, PUB_CREDENTIAL_SET));
 160         this.privCredentials = Collections.synchronizedSet
 161                         (new SecureSet<Object>(this, PRIV_CREDENTIAL_SET));
 162     }
 163 
 164     /**
 165      * Create an instance of a {@code Subject} with
 166      * Principals and credentials.
 167      *
 168      * <p> The Principals and credentials from the specified Sets
 169      * are copied into newly constructed Sets.
 170      * These newly created Sets check whether this {@code Subject}
 171      * has been set read-only before permitting subsequent modifications.
 172      * The newly created Sets also prevent illegal modifications
 173      * by ensuring that callers have sufficient permissions.  These Sets
 174      * also prohibit null elements, and attempts to add or query a null
 175      * element will result in a {@code NullPointerException}.
 176      *
 177      * <p> To modify the Principals Set, the caller must have
 178      * {@code AuthPermission("modifyPrincipals")}.
 179      * To modify the public credential Set, the caller must have
 180      * {@code AuthPermission("modifyPublicCredentials")}.
 181      * To modify the private credential Set, the caller must have


 189      *
 190      * @param pubCredentials the {@code Set} of public credentials
 191      *          to be associated with this {@code Subject}.
 192      *
 193      * @param privCredentials the {@code Set} of private credentials
 194      *          to be associated with this {@code Subject}.
 195      *
 196      * @throws NullPointerException if the specified
 197      *          {@code principals}, {@code pubCredentials},
 198      *          or {@code privCredentials} are {@code null},
 199      *          or a null value exists within any of these three
 200      *          Sets.
 201      */
 202     public Subject(boolean readOnly, Set<? extends Principal> principals,
 203                    Set<?> pubCredentials, Set<?> privCredentials)
 204     {
 205         collectionNullClean(principals);
 206         collectionNullClean(pubCredentials);
 207         collectionNullClean(privCredentials);
 208 
 209         this.principals = Collections.synchronizedSet(new SecureSet<Principal>
 210                                 (this, PRINCIPAL_SET, principals));
 211         this.pubCredentials = Collections.synchronizedSet(new SecureSet<Object>
 212                                 (this, PUB_CREDENTIAL_SET, pubCredentials));
 213         this.privCredentials = Collections.synchronizedSet(new SecureSet<Object>
 214                                 (this, PRIV_CREDENTIAL_SET, privCredentials));
 215         this.readOnly = readOnly;
 216     }
 217 
 218     /**
 219      * Set this {@code Subject} to be read-only.
 220      *
 221      * <p> Modifications (additions and removals) to this Subject's
 222      * {@code Principal} {@code Set} and
 223      * credential Sets will be disallowed.
 224      * The {@code destroy} operation on this Subject's credentials will
 225      * still be permitted.
 226      *
 227      * <p> Subsequent attempts to modify the Subject's {@code Principal}
 228      * and credential Sets will result in an
 229      * {@code IllegalStateException} being thrown.
 230      * Also, once a {@code Subject} is read-only,
 231      * it can not be reset to being writable again.
 232      *
 233      * @throws SecurityException if a security manager is installed and the


 275      *          caller does not have an
 276      *          {@link AuthPermission#AuthPermission(String)
 277      *          AuthPermission("getSubject")} permission to get the
 278      *          {@code Subject}.
 279      *
 280      * @throws NullPointerException if the provided
 281      *          {@code AccessControlContext} is {@code null}.
 282      */
 283     public static Subject getSubject(final AccessControlContext acc) {
 284 
 285         java.lang.SecurityManager sm = System.getSecurityManager();
 286         if (sm != null) {
 287             sm.checkPermission(AuthPermissionHolder.GET_SUBJECT_PERMISSION);
 288         }
 289 
 290         Objects.requireNonNull(acc, ResourcesMgr.getString
 291                 ("invalid.null.AccessControlContext.provided"));
 292 
 293         // return the Subject from the DomainCombiner of the provided context
 294         return AccessController.doPrivileged
 295             (new java.security.PrivilegedAction<Subject>() {
 296             public Subject run() {
 297                 DomainCombiner dc = acc.getDomainCombiner();
 298                 if (!(dc instanceof SubjectDomainCombiner)) {
 299                     return null;
 300                 }
 301                 SubjectDomainCombiner sdc = (SubjectDomainCombiner)dc;
 302                 return sdc.getSubject();
 303             }
 304         });
 305     }
 306 
 307     /**
 308      * Perform work as a particular {@code Subject}.
 309      *
 310      * <p> This method first retrieves the current Thread's
 311      * {@code AccessControlContext} via
 312      * {@code AccessController.getContext},
 313      * and then instantiates a new {@code AccessControlContext}
 314      * using the retrieved context along with a new
 315      * {@code SubjectDomainCombiner} (constructed using


 538         Objects.requireNonNull(action,
 539                 ResourcesMgr.getString("invalid.null.action.provided"));
 540 
 541         // set up the new Subject-based AccessControlContext for doPrivileged
 542         final AccessControlContext callerAcc =
 543                 (acc == null ?
 544                 new AccessControlContext(NULL_PD_ARRAY) :
 545                 acc);
 546 
 547         // call doPrivileged and push this new context on the stack
 548         return java.security.AccessController.doPrivileged
 549                                         (action,
 550                                         createContext(subject, callerAcc));
 551     }
 552 
 553     private static AccessControlContext createContext(final Subject subject,
 554                                         final AccessControlContext acc) {
 555 
 556 
 557         return java.security.AccessController.doPrivileged
 558             (new java.security.PrivilegedAction<AccessControlContext>() {
 559             public AccessControlContext run() {
 560                 if (subject == null) {
 561                     return new AccessControlContext(acc, null);
 562                 } else {
 563                     return new AccessControlContext
 564                                         (acc,
 565                                         new SubjectDomainCombiner(subject));
 566             }
 567             }
 568         });
 569     }
 570 
 571     /**
 572      * Return the {@code Set} of Principals associated with this
 573      * {@code Subject}.  Each {@code Principal} represents
 574      * an identity for this {@code Subject}.
 575      *
 576      * <p> The returned {@code Set} is backed by this Subject's
 577      * internal {@code Principal} {@code Set}.  Any modification
 578      * to the returned {@code Set} affects the internal


 784      */
 785     @Override
 786     public boolean equals(Object o) {
 787 
 788         if (o == null) {
 789             return false;
 790         }
 791 
 792         if (this == o) {
 793             return true;
 794         }
 795 
 796         if (o instanceof Subject) {
 797 
 798             final Subject that = (Subject)o;
 799 
 800             // check the principal and credential sets
 801             Set<Principal> thatPrincipals;
 802             synchronized(that.principals) {
 803                 // avoid deadlock from dual locks
 804                 thatPrincipals = new HashSet<Principal>(that.principals);
 805             }
 806             if (!principals.equals(thatPrincipals)) {
 807                 return false;
 808             }
 809 
 810             Set<Object> thatPubCredentials;
 811             synchronized(that.pubCredentials) {
 812                 // avoid deadlock from dual locks
 813                 thatPubCredentials = new HashSet<Object>(that.pubCredentials);
 814             }
 815             if (!pubCredentials.equals(thatPubCredentials)) {
 816                 return false;
 817             }
 818 
 819             Set<Object> thatPrivCredentials;
 820             synchronized(that.privCredentials) {
 821                 // avoid deadlock from dual locks
 822                 thatPrivCredentials = new HashSet<Object>(that.privCredentials);
 823             }
 824             if (!privCredentials.equals(thatPrivCredentials)) {
 825                 return false;
 826             }
 827             return true;
 828         }
 829         return false;
 830     }
 831 
 832     /**
 833      * Return the String representation of this {@code Subject}.
 834      *
 835      * @return the String representation of this {@code Subject}.
 836      */
 837     @Override
 838     public String toString() {
 839         return toString(true);
 840     }
 841 
 842     /**


 953     }
 954 
 955     /**
 956      * Reads this object from a stream (i.e., deserializes it)
 957      */
 958     @SuppressWarnings("unchecked")
 959     private void readObject(java.io.ObjectInputStream s)
 960                 throws java.io.IOException, ClassNotFoundException {
 961 
 962         ObjectInputStream.GetField gf = s.readFields();
 963 
 964         readOnly = gf.get("readOnly", false);
 965 
 966         Set<Principal> inputPrincs = (Set<Principal>)gf.get("principals", null);
 967 
 968         Objects.requireNonNull(inputPrincs,
 969                 ResourcesMgr.getString("invalid.null.input.s."));
 970 
 971         // Rewrap the principals into a SecureSet
 972         try {
 973             principals = Collections.synchronizedSet(new SecureSet<Principal>
 974                                 (this, PRINCIPAL_SET, inputPrincs));
 975         } catch (NullPointerException npe) {
 976             // Sometimes people deserialize the principals set only.
 977             // Subject is not accessible, so just don't fail.
 978             principals = Collections.synchronizedSet
 979                         (new SecureSet<Principal>(this, PRINCIPAL_SET));
 980         }
 981 
 982         // The Credential {@code Set} is not serialized, but we do not
 983         // want the default deserialization routine to set it to null.
 984         this.pubCredentials = Collections.synchronizedSet
 985                         (new SecureSet<Object>(this, PUB_CREDENTIAL_SET));
 986         this.privCredentials = Collections.synchronizedSet
 987                         (new SecureSet<Object>(this, PRIV_CREDENTIAL_SET));
 988     }
 989 
 990     /**
 991      * Tests for null-clean collections (both non-null reference and
 992      * no null elements)
 993      *
 994      * @param coll A {@code Collection} to be tested for null references
 995      *
 996      * @throws NullPointerException if the specified collection is either
 997      *            {@code null} or contains a {@code null} element
 998      */
 999     private static void collectionNullClean(Collection<?> coll) {
1000         boolean hasNullElements = false;
1001 
1002         Objects.requireNonNull(coll,
1003                 ResourcesMgr.getString("invalid.null.input.s."));
1004 
1005         try {
1006             hasNullElements = coll.contains(null);
1007         } catch (NullPointerException npe) {


1480             final Iterator<?> iterator;
1481             switch(which) {
1482             case Subject.PRINCIPAL_SET:
1483                 iterator = Subject.this.principals.iterator();
1484                 break;
1485             case Subject.PUB_CREDENTIAL_SET:
1486                 iterator = Subject.this.pubCredentials.iterator();
1487                 break;
1488             default:
1489                 iterator = Subject.this.privCredentials.iterator();
1490                 break;
1491             }
1492 
1493             // Check whether the caller has permisson to get
1494             // credentials of Class c
1495 
1496             while (iterator.hasNext()) {
1497                 Object next;
1498                 if (which == Subject.PRIV_CREDENTIAL_SET) {
1499                     next = java.security.AccessController.doPrivileged
1500                         (new java.security.PrivilegedAction<Object>() {
1501                         public Object run() {
1502                             return iterator.next();
1503                         }
1504                     });
1505                 } else {
1506                     next = iterator.next();
1507                 }
1508                 if (c.isAssignableFrom(next.getClass())) {
1509                     if (which != Subject.PRIV_CREDENTIAL_SET) {
1510                         set.add((T)next);
1511                     } else {
1512                         // Check permission for private creds
1513                         SecurityManager sm = System.getSecurityManager();
1514                         if (sm != null) {
1515                             sm.checkPermission(new PrivateCredentialPermission
1516                                                 (next.getClass().getName(),
1517                                                 Subject.this.getPrincipals()));
1518                         }
1519                         set.add((T)next);
1520                     }




 137      * with an empty {@code Set} of Principals and empty
 138      * Sets of public and private credentials.
 139      *
 140      * <p> The newly constructed Sets check whether this {@code Subject}
 141      * has been set read-only before permitting subsequent modifications.
 142      * The newly created Sets also prevent illegal modifications
 143      * by ensuring that callers have sufficient permissions.  These Sets
 144      * also prohibit null elements, and attempts to add or query a null
 145      * element will result in a {@code NullPointerException}.
 146      *
 147      * <p> To modify the Principals Set, the caller must have
 148      * {@code AuthPermission("modifyPrincipals")}.
 149      * To modify the public credential Set, the caller must have
 150      * {@code AuthPermission("modifyPublicCredentials")}.
 151      * To modify the private credential Set, the caller must have
 152      * {@code AuthPermission("modifyPrivateCredentials")}.
 153      */
 154     public Subject() {
 155 
 156         this.principals = Collections.synchronizedSet
 157                         (new SecureSet<>(this, PRINCIPAL_SET));
 158         this.pubCredentials = Collections.synchronizedSet
 159                         (new SecureSet<>(this, PUB_CREDENTIAL_SET));
 160         this.privCredentials = Collections.synchronizedSet
 161                         (new SecureSet<>(this, PRIV_CREDENTIAL_SET));
 162     }
 163 
 164     /**
 165      * Create an instance of a {@code Subject} with
 166      * Principals and credentials.
 167      *
 168      * <p> The Principals and credentials from the specified Sets
 169      * are copied into newly constructed Sets.
 170      * These newly created Sets check whether this {@code Subject}
 171      * has been set read-only before permitting subsequent modifications.
 172      * The newly created Sets also prevent illegal modifications
 173      * by ensuring that callers have sufficient permissions.  These Sets
 174      * also prohibit null elements, and attempts to add or query a null
 175      * element will result in a {@code NullPointerException}.
 176      *
 177      * <p> To modify the Principals Set, the caller must have
 178      * {@code AuthPermission("modifyPrincipals")}.
 179      * To modify the public credential Set, the caller must have
 180      * {@code AuthPermission("modifyPublicCredentials")}.
 181      * To modify the private credential Set, the caller must have


 189      *
 190      * @param pubCredentials the {@code Set} of public credentials
 191      *          to be associated with this {@code Subject}.
 192      *
 193      * @param privCredentials the {@code Set} of private credentials
 194      *          to be associated with this {@code Subject}.
 195      *
 196      * @throws NullPointerException if the specified
 197      *          {@code principals}, {@code pubCredentials},
 198      *          or {@code privCredentials} are {@code null},
 199      *          or a null value exists within any of these three
 200      *          Sets.
 201      */
 202     public Subject(boolean readOnly, Set<? extends Principal> principals,
 203                    Set<?> pubCredentials, Set<?> privCredentials)
 204     {
 205         collectionNullClean(principals);
 206         collectionNullClean(pubCredentials);
 207         collectionNullClean(privCredentials);
 208 
 209         this.principals = Collections.synchronizedSet(new SecureSet<>
 210                                 (this, PRINCIPAL_SET, principals));
 211         this.pubCredentials = Collections.synchronizedSet(new SecureSet<>
 212                                 (this, PUB_CREDENTIAL_SET, pubCredentials));
 213         this.privCredentials = Collections.synchronizedSet(new SecureSet<>
 214                                 (this, PRIV_CREDENTIAL_SET, privCredentials));
 215         this.readOnly = readOnly;
 216     }
 217 
 218     /**
 219      * Set this {@code Subject} to be read-only.
 220      *
 221      * <p> Modifications (additions and removals) to this Subject's
 222      * {@code Principal} {@code Set} and
 223      * credential Sets will be disallowed.
 224      * The {@code destroy} operation on this Subject's credentials will
 225      * still be permitted.
 226      *
 227      * <p> Subsequent attempts to modify the Subject's {@code Principal}
 228      * and credential Sets will result in an
 229      * {@code IllegalStateException} being thrown.
 230      * Also, once a {@code Subject} is read-only,
 231      * it can not be reset to being writable again.
 232      *
 233      * @throws SecurityException if a security manager is installed and the


 275      *          caller does not have an
 276      *          {@link AuthPermission#AuthPermission(String)
 277      *          AuthPermission("getSubject")} permission to get the
 278      *          {@code Subject}.
 279      *
 280      * @throws NullPointerException if the provided
 281      *          {@code AccessControlContext} is {@code null}.
 282      */
 283     public static Subject getSubject(final AccessControlContext acc) {
 284 
 285         java.lang.SecurityManager sm = System.getSecurityManager();
 286         if (sm != null) {
 287             sm.checkPermission(AuthPermissionHolder.GET_SUBJECT_PERMISSION);
 288         }
 289 
 290         Objects.requireNonNull(acc, ResourcesMgr.getString
 291                 ("invalid.null.AccessControlContext.provided"));
 292 
 293         // return the Subject from the DomainCombiner of the provided context
 294         return AccessController.doPrivileged
 295             (new java.security.PrivilegedAction<>() {
 296             public Subject run() {
 297                 DomainCombiner dc = acc.getDomainCombiner();
 298                 if (!(dc instanceof SubjectDomainCombiner)) {
 299                     return null;
 300                 }
 301                 SubjectDomainCombiner sdc = (SubjectDomainCombiner)dc;
 302                 return sdc.getSubject();
 303             }
 304         });
 305     }
 306 
 307     /**
 308      * Perform work as a particular {@code Subject}.
 309      *
 310      * <p> This method first retrieves the current Thread's
 311      * {@code AccessControlContext} via
 312      * {@code AccessController.getContext},
 313      * and then instantiates a new {@code AccessControlContext}
 314      * using the retrieved context along with a new
 315      * {@code SubjectDomainCombiner} (constructed using


 538         Objects.requireNonNull(action,
 539                 ResourcesMgr.getString("invalid.null.action.provided"));
 540 
 541         // set up the new Subject-based AccessControlContext for doPrivileged
 542         final AccessControlContext callerAcc =
 543                 (acc == null ?
 544                 new AccessControlContext(NULL_PD_ARRAY) :
 545                 acc);
 546 
 547         // call doPrivileged and push this new context on the stack
 548         return java.security.AccessController.doPrivileged
 549                                         (action,
 550                                         createContext(subject, callerAcc));
 551     }
 552 
 553     private static AccessControlContext createContext(final Subject subject,
 554                                         final AccessControlContext acc) {
 555 
 556 
 557         return java.security.AccessController.doPrivileged
 558             (new java.security.PrivilegedAction<>() {
 559             public AccessControlContext run() {
 560                 if (subject == null) {
 561                     return new AccessControlContext(acc, null);
 562                 } else {
 563                     return new AccessControlContext
 564                                         (acc,
 565                                         new SubjectDomainCombiner(subject));
 566             }
 567             }
 568         });
 569     }
 570 
 571     /**
 572      * Return the {@code Set} of Principals associated with this
 573      * {@code Subject}.  Each {@code Principal} represents
 574      * an identity for this {@code Subject}.
 575      *
 576      * <p> The returned {@code Set} is backed by this Subject's
 577      * internal {@code Principal} {@code Set}.  Any modification
 578      * to the returned {@code Set} affects the internal


 784      */
 785     @Override
 786     public boolean equals(Object o) {
 787 
 788         if (o == null) {
 789             return false;
 790         }
 791 
 792         if (this == o) {
 793             return true;
 794         }
 795 
 796         if (o instanceof Subject) {
 797 
 798             final Subject that = (Subject)o;
 799 
 800             // check the principal and credential sets
 801             Set<Principal> thatPrincipals;
 802             synchronized(that.principals) {
 803                 // avoid deadlock from dual locks
 804                 thatPrincipals = new HashSet<>(that.principals);
 805             }
 806             if (!principals.equals(thatPrincipals)) {
 807                 return false;
 808             }
 809 
 810             Set<Object> thatPubCredentials;
 811             synchronized(that.pubCredentials) {
 812                 // avoid deadlock from dual locks
 813                 thatPubCredentials = new HashSet<>(that.pubCredentials);
 814             }
 815             if (!pubCredentials.equals(thatPubCredentials)) {
 816                 return false;
 817             }
 818 
 819             Set<Object> thatPrivCredentials;
 820             synchronized(that.privCredentials) {
 821                 // avoid deadlock from dual locks
 822                 thatPrivCredentials = new HashSet<>(that.privCredentials);
 823             }
 824             if (!privCredentials.equals(thatPrivCredentials)) {
 825                 return false;
 826             }
 827             return true;
 828         }
 829         return false;
 830     }
 831 
 832     /**
 833      * Return the String representation of this {@code Subject}.
 834      *
 835      * @return the String representation of this {@code Subject}.
 836      */
 837     @Override
 838     public String toString() {
 839         return toString(true);
 840     }
 841 
 842     /**


 953     }
 954 
 955     /**
 956      * Reads this object from a stream (i.e., deserializes it)
 957      */
 958     @SuppressWarnings("unchecked")
 959     private void readObject(java.io.ObjectInputStream s)
 960                 throws java.io.IOException, ClassNotFoundException {
 961 
 962         ObjectInputStream.GetField gf = s.readFields();
 963 
 964         readOnly = gf.get("readOnly", false);
 965 
 966         Set<Principal> inputPrincs = (Set<Principal>)gf.get("principals", null);
 967 
 968         Objects.requireNonNull(inputPrincs,
 969                 ResourcesMgr.getString("invalid.null.input.s."));
 970 
 971         // Rewrap the principals into a SecureSet
 972         try {
 973             principals = Collections.synchronizedSet(new SecureSet<>
 974                                 (this, PRINCIPAL_SET, inputPrincs));
 975         } catch (NullPointerException npe) {
 976             // Sometimes people deserialize the principals set only.
 977             // Subject is not accessible, so just don't fail.
 978             principals = Collections.synchronizedSet
 979                         (new SecureSet<>(this, PRINCIPAL_SET));
 980         }
 981 
 982         // The Credential {@code Set} is not serialized, but we do not
 983         // want the default deserialization routine to set it to null.
 984         this.pubCredentials = Collections.synchronizedSet
 985                         (new SecureSet<>(this, PUB_CREDENTIAL_SET));
 986         this.privCredentials = Collections.synchronizedSet
 987                         (new SecureSet<>(this, PRIV_CREDENTIAL_SET));
 988     }
 989 
 990     /**
 991      * Tests for null-clean collections (both non-null reference and
 992      * no null elements)
 993      *
 994      * @param coll A {@code Collection} to be tested for null references
 995      *
 996      * @throws NullPointerException if the specified collection is either
 997      *            {@code null} or contains a {@code null} element
 998      */
 999     private static void collectionNullClean(Collection<?> coll) {
1000         boolean hasNullElements = false;
1001 
1002         Objects.requireNonNull(coll,
1003                 ResourcesMgr.getString("invalid.null.input.s."));
1004 
1005         try {
1006             hasNullElements = coll.contains(null);
1007         } catch (NullPointerException npe) {


1480             final Iterator<?> iterator;
1481             switch(which) {
1482             case Subject.PRINCIPAL_SET:
1483                 iterator = Subject.this.principals.iterator();
1484                 break;
1485             case Subject.PUB_CREDENTIAL_SET:
1486                 iterator = Subject.this.pubCredentials.iterator();
1487                 break;
1488             default:
1489                 iterator = Subject.this.privCredentials.iterator();
1490                 break;
1491             }
1492 
1493             // Check whether the caller has permisson to get
1494             // credentials of Class c
1495 
1496             while (iterator.hasNext()) {
1497                 Object next;
1498                 if (which == Subject.PRIV_CREDENTIAL_SET) {
1499                     next = java.security.AccessController.doPrivileged
1500                         (new java.security.PrivilegedAction<>() {
1501                         public Object run() {
1502                             return iterator.next();
1503                         }
1504                     });
1505                 } else {
1506                     next = iterator.next();
1507                 }
1508                 if (c.isAssignableFrom(next.getClass())) {
1509                     if (which != Subject.PRIV_CREDENTIAL_SET) {
1510                         set.add((T)next);
1511                     } else {
1512                         // Check permission for private creds
1513                         SecurityManager sm = System.getSecurityManager();
1514                         if (sm != null) {
1515                             sm.checkPermission(new PrivateCredentialPermission
1516                                                 (next.getClass().getName(),
1517                                                 Subject.this.getPrincipals()));
1518                         }
1519                         set.add((T)next);
1520                     }


< prev index next >