7

私はBackboneとKnockoutとKnockback(ko + bbブリッジライブラリ)を使用してかなり大きなcmsタイプのアプリケーションを構築しており、アクセス許可を抽象化するための良い方法を見つけようとしています。また、小説を事前に申し訳ありません。

まず第一に、これはかなり非標準的なアーキテクチャであり、あなたが尋ねるかもしれない2番目の質問-EmberやAngularのようなより包括的なものを使用してみませんか?取ったポイント。それがこの時点でのことです。:)

これが私の悩みの種です。パーミッションのために、コントローラーレベルとビューモデルレベルの両方でエレガントなAPIが必要です。

次のようなオブジェクトを利用できます。

{
   'api/pages': {
     create: true, read: true, update: true, destroy: true
   },
   'api/links': {
     create: false, read: true, update: false, destroy: false
   }
   ...
}

そのため、ルーター/コントローラーで、コレクション/モデル/ビューモデルを更新し、既存のビューでカスタマイズされたレンダリングメソッドを呼び出しています。ビューは、ビューモデルのリリースなどを処理します。

initialize: function() {
  this.pages = new PagesCollection();
  this.links = new LinksCollection();
},

list: function() {
  var vm = new PageListViewmodel(this.pages, this.links);
  // adminPage method is available through inheritance
  this.adminPage('path/to/template', vm); // delegates to kb.renderTemplate under the hood.
}

したがって、これに関する問題は、これらのコレクションが完全に構造化されていないことです。ルーターはそれらについて何も知りません。

しかし、私が必要としているのは、特定のリソースの表示が許可されていない場合に、許可されていないページにリダイレクトすることです。

上記の例では、フィルターの前後にコーディングすることを考えましたか?しかし、各ルーターメソッドがアクセスしようとしているものをどこで指定しますか?

list: function() {
  this.authorize([this.pages, this.links], ['read'], function(pages, links) {
    // return view.
  });
}

前のコードは本当に不器用です。

より単純なビューモデルの場合、私は次のようなことを行うことを考えました-alaRubyのCanCan:

this.currentUser.can('read', collection) // true or false
// can() would just look at the endpoint and compare to my perms object.
4

2 に答える 2

4

ルーターを拡張してルートコールバックをラップし、アクションを許可する前に有効性チェックを実行できます。

var Router = Backbone.Router.extend({
    routes: {
        "app/*perm": "go"
    },

    route: function(route, name, callback) {
        if (!callback) callback = this[name];

        var f = function() {
            var perms = this.authorized(Backbone.history.getFragment());
            if (perms === true) {
                callback.apply(this, arguments);
            } else {
                this.trigger('denied', perms);
            }
        };
        return Backbone.Router.prototype.route.call(this, route, name, f);
    },

    authorized: function(path) {
        // check if the path is authorized
    },

    go: function(perm) {
       // perform action
    }
});

パスが許可されている場合、ルートは通常どおりに実行され、許可されていない場合は拒否されたイベントがトリガーされます。

このauthorizedメソッドは、パーミッションオブジェクトにマップされたパスのリストに基づくことができます。

var permissions = {
   'api/pages': {
     create: true, read: true, update: true, destroy: true
   },
   'api/links': {
     create: false, read: true, update: false, destroy: false
   }
}
var Router = Backbone.Router.extend({
    routes: {
        "app/*perm": "go"
    },

    // protected paths, with the corresponding entry in the permissions object
    permissionsMap: {
        "app/pages": 'api/pages',
        "app/links": 'api/links',
    },

    route: function(route, name, callback) {
        // see above
    },

    // returns true if the path is allowed
    // returns an object with the path and the permission key used if not
    authorized: function(path) {
        var paths, match, permkey, perms;

        // find an entry for the current path
        paths = _.keys(this.permissionsMap);
        match = _.find(paths, function(p) {
            return path.indexOf(p)===0;            
        });
        if (!match) return true;

        //check if the read permission is allowed
        permkey = this.permissionsMap[match];
        if (!permissions[permkey]) return true;
        if (permissions[permkey].read) return true;

        return {
            path: path,
            permission: permkey
        };
    },

    go: function(perm) {}
});

そしてデモhttp://jsfiddle.net/t2vMA/1/

于 2013-02-10T13:01:18.407 に答える
1

Nikoshrの答えは、私に何かを実行するための何かを与えてくれました。私は実際に自分自身をオーバーライドするとは思いませんでしたroute。しかし、これが私の解決策です。質問でそれについて言及する必要がありましたが、ルーターのアクションで複数のコレクションが必要になる場合があります。

このコードは非常にラフで、テストが必要ですが、機能します。ここでフィドル。

関連する部分は次のとおりです-これらの2つの方法が承認を処理します。

authorize: function(namedRoute) {
  if (this.permissions && this.collections) {
    var perms = this.permissions[namedRoute];
    if (!perms) {
      perms = {};
      // if nothing is specified for a particular  route, we
      // assume read access required for all registered controllers.
      _.each(_.keys(this.collections), function(key) {
        return perms[key] = [];
      });
    }

    var authorized = _.chain(perms)
      .map(function(reqPerms, collKey) {
        var collection = this.collections[collKey],
            permKey = _.result(collection, 'url');

        // We implicitly check for 'read'
        if (!_.contains('read')) {
          reqPerms.push('read');
        }

        return _.every(reqPerms, function(ability) {
          return userPermissions[permKey][ability];
        });
      }, this)
      .every(function(auth){ return auth; })
      .value();
    return authorized;
  }
  return true;
},
route: function(route, name, callback) {
  if (!callback) { callback = this[name]; }
  var action = function() {
    // allow anonymous routes through auth check.
    if (!name || this.authorize(name)) {
      callback.apply(this, arguments);
    } else {
      this.trigger('denied');
    }
  }
  Backbone.Router.prototype.route.call(this, route, name, action);
  return this;
}

また、各コントローラー/ルーターはpermルーターから継承し、各アクションの権限は次のようにマップされます。

// Setup
routes: {
  'list'     : 'list',
  'list/:id' : 'detail',
  'create'   : 'create'
},

// Collection are registered so we can
// keep track of what actions use them
collections: {
  pages: new PagesCollection([{id:1, title: 'stuff'}]),
  links: new LinksCollection([{id:1, link: 'things'}])
},

// If a router method is not defined,
// 'read' access is assumed to be
// required for all registered collections.
permissions: {
  detail: {
    pages: ['update'],
    links: ['update']
  },
  create: {
    pages: ['create'],
    links: ['create', 'update']
  }
},
于 2013-02-12T16:42:37.073 に答える