2008-09-28 10 views
7

Je suis en train d'implémenter un service WCF sécurisé. L'authentification est effectuée à l'aide du nom d'utilisateur/mot de passe ou des informations d'identification Windows. Le service est hébergé dans un processus de service Windows. Maintenant, j'essaie de trouver la meilleure façon d'implémenter autorisation pour chaque opération de service.Modèles d'autorisation de service WCF

Par exemple, considérons la méthode suivante:

public EntityInfo GetEntityInfo(string entityId); 

Comme vous le savez, dans WCF, il y a un objet OperationContext à partir duquel vous pouvez récupérer les informations d'identification de sécurité transmise par l'appelant/client. Maintenant, l'authentification aurait déjà fini au moment où la première ligne de la méthode est appelée. Cependant, comment pouvons-nous mettre en œuvre l'autorisation si la décision dépend des données d'entrée elle-même? Par exemple, dans le cas ci-dessus, dites utilisateurs 'admin' (dont les permissions etc sont stockées dans une base de données), sont autorisés à obtenir des informations d'entité, et d'autres utilisateurs ne devraient pas être autorisés ... où mettons-nous les vérifications d'autorisation?

dire que nous l'avons mis dans la première ligne de la méthode comme ceci:

CheckAccessPermission(PermissionType.GetEntity, user, entityId) //user is pulled from the current OperationContext 

Maintenant, il y a quelques questions:

  1. Ne nous validons la entityID (par exemple vérifier valeur nulle/vide, etc.) AVANT le contrôle d'autorisation ou à l'intérieur du contrôle d'autorisation? En d'autres termes, si les contrôles d'autorisation doivent être inclus dans chaque méthode, est-ce un bon modèle? Quel devrait être le premier argument - validation ou autorisation?

  2. Comment est-ce que nous testons un service WCF unitaire lorsque les vérifications d'autorisation sont partout comme ceci, et nous n'avons pas un OperationContext dans le test unitaire!? (En supposant que j'essaie de tester cette implémentation de classe de service directement sans aucune configuration de WCF).

Des idées les gars?

Répondre

3

Pour la question 1, faites d'abord l'autorisation. Aucun code (sous votre contrôle) ne devrait être exécuté avant l'autorisation de maintenir la sécurité la plus stricte. L'exemple de Paul ci-dessus est excellent.

Pour la question 2, vous pourriez gérer cela en sous-classant votre implémentation de service concrète. Faites de la véritable implémentation de la logique métier une classe abstraite avec une méthode abstraite "CheckPermissions" comme vous l'avez mentionné plus haut. Créez ensuite 2 sous-classes, une pour l'utilisation de WCF, et une (très isolée dans une DLL non déployée) qui renvoie true (ou ce que vous voulez dans vos tests unitaires). Exemple (notez que ces fichiers ne devraient pas être dans le même fichier ou même dans la même DLL!).):

public abstract class MyServiceImpl 
{ 
    public void MyMethod(string entityId) 
    { 
     CheckPermissions(entityId); 
     //move along... 
    } 
    protected abstract bool CheckPermissions(string entityId); 
} 

public class MyServiceUnitTest 
{ 
    private bool CheckPermissions(string entityId) 
    { 
     return true; 
    } 
} 

public class MyServiceMyAuth 
{ 
    private bool CheckPermissions(string entityId) 
    { 
     //do some custom authentication 
     return true; 
    } 
} 

Ensuite, votre déploiement de WCF utilise la classe « MyServiceMyAuth », et vous faites vos tests unitaires contre l'autre.

6

Pour la question 1, il est préférable d'effectuer l'autorisation en premier. De cette façon, vous ne perdez pas les messages d'erreur de validation à des utilisateurs non autorisés. BTW, au lieu d'utiliser une méthode d'authentification locale (ce que je suppose être votre CheckAccessPermission), vous pourriez être en mesure de vous connecter au support prêt à l'emploi de WCF pour les fournisseurs de rôles ASP.NET. Une fois cela fait, vous effectuez l'autorisation via OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.IsInRole(). PrimaryIdentity est un IPrincipal.

+0

Merci Paul. Le problème avec l'autorisation d'abord, est le suivant: comment autoriser un utilisateur si nous devons vérifier les permissions en fonction des arguments d'entrée? Ne devons-nous pas d'abord valider ces arguments avant de l'utiliser pour l'autorisation? – Krishna

+0

L'autorisation ne doit dépendre que de l'identité de l'utilisateur. Si cela dépend des arguments d'entrée, alors l'appelant peut envoyer toutes les valeurs dont il a besoin pour obtenir l'autorisation qu'il souhaite, de sorte que votre vérification d'autorisation perd toute signification. –

+6

Non. Dites que je veux accéder à un objet avec l'ID 'abc1'. Je suis 'user1'. L'autorisation décide si 'l'utilisateur 1' peut accéder à l'objet 'abc1'. donc la première chose à faire sera de valider le paramètre qui contient la chaîne de l'id de l'objet! – Krishna

6

A propos de la question n ° 2, je referais ce à l'aide d'injection de dépendance et mettre en place quelque chose de votre implémentation de service comme celui-ci:

class MyService : IMyService 
{ 
    public MyService() : this(new UserAuthorization()) { } 
    public MyService(IAuthorization auth) { _auth = auth; } 

    private IAuthorization _auth; 

    public EntityInfo GetEntityInfo(string entityId) 
    { 
      _auth.CheckAccessPermission(PermissionType.GetEntity, 
        user, entityId); 

      //Get the entity info 
    } 
} 

Notez que IAuthorization est une interface que vous définiriez. Comme vous allez tester directement le type de service (c'est-à-dire sans l'exécuter dans l'infrastructure d'hébergement WCF), vous configurez simplement votre service pour utiliser un type IAuthorization factice qui autorise tous les appels. Cependant, un test encore meilleur est de se moquer de l'IAuthorization et de tester qu'il est appelé quand et avec les paramètres que vous attendez. Cela vous permet de tester que vos appels aux méthodes d'autorisation sont valides, ainsi que la méthode elle-même.

La séparation de l'autorisation dans son propre type vous permet également de tester plus facilement qu'elle est correcte. Dans mon expérience (bien que limitée), l'utilisation de "patterns" DI vous donne une bien meilleure séparation des préoccupations et de la testabilité dans vos types, tout en conduisant à une interface plus propre (ceci est évidemment ouvert au débat).

Mon cadre de simulation préféré est RhinoMocks qui est gratuit et possède une très belle interface fluide, mais il y en a beaucoup d'autres. Si vous souhaitez en savoir plus sur DI ici quelques bonnes amorces et cadres .Net: