Firebase数据库规则-基于自定义声明的规则

时间:2020-02-12 14:46:17

标签: firebase firebase-realtime-database firebase-authentication firebase-security

我有一个Firebase实时数据库。当前它包含两个节点admincommon

{
  "admin" : {
    "adminval" : 9898574632,
    "adminval1" : 645354536,
    "adminval2" : 7776756433
  },
  "common" : {
    "commonval" : 123433221
  }
}

我向每个用户自定义声明roles添加了一个描述用户在我的系统中角色的声明。看起来像这样

{'roles': ['ROLE_ADMIN', 'ROLE_USER']}

现在,我想限制访问,以便仅具有声明ROLE_ADMIN的用户才能读/写admin节点,并且具有任一角色的用户都可以读/写节点common。 / p>

该怎么做?我尝试过类似的事情:

{
    "rules": {
       "admin": {
           ".read": "auth.token.roles.contains('ROLE_ADMIN')",
           ".write": "auth.token.roles.contains('ROLE_ADMIN')"
       }
       "common": {
           ".read": "auth.token.roles.contains('ROLE_USER') || auth.token.rules.contains('ROLE_ADMIN')",
           ".write": "auth.token.roles.contains('ROLE_USER') || auth.token.rules.contains('ROLE_ADMIN')"
       }
    }
  }

2 个答案:

答案 0 :(得分:0)

我从未在自定义声明中使用过数组,因此不确定其工作方式或为什么对您不起作用。

但是我确实注意到您在规则中混合了基于角色的访问控制和基于级别的访问控制,因此想给出一些一般性的提示/观察:

  • 您要同时为管理员标记ROLE_ADMINROLE_USER
  • 但是随后您在common节点中有一个OR,请检查其中一个角色。这意味着也无需向管理员提供ROLE_USER
  • 请记住,自定义声明限制为1000个字节,并随每个请求一起发送。我强烈建议删除ROLE_前缀,因为声明的roles名称中已经暗示了该前缀。
  • 如果您具有管理员可以看到的比普通用户更多的通用逻辑,请考虑使用数字access_level声明。假设普通用户是level: 1,而管理员是level: 2。然后,您可以在索赔中检查最低水平:".read": "auth.token.access_level >= 1"

答案 1 :(得分:0)

恐怕我们不能在实时数据库安全规则中使用export const createProject = (project) => { return (dispatch, getState, { getFirebase, getFirestore }) => { const firestore = getFirestore(); firestore.collection('Projects').add({ ...project, createdAt: new Date() }).then(() => dispatch({ type: 'CREATE_PROJECT', project })).catch(err => dispatch({ type: 'CREATE_ERROR', err })) } } public class Login_A extends Activity { FirebaseAuth mAuth; FirebaseUser firebaseUser; SharedPreferences sharedPreferences; View top_view; TextView login_title_txt; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); if (Build.VERSION.SDK_INT == 26) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); } getWindow().setBackgroundDrawable( new ColorDrawable(android.graphics.Color.TRANSPARENT)); this.getWindow() .setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_login); mAuth = FirebaseAuth.getInstance(); firebaseUser=mAuth.getCurrentUser(); // if the user is already login through facebook then we will logout the user automatically LoginManager.getInstance().logOut(); sharedPreferences=getSharedPreferences(Variables.pref_name,MODE_PRIVATE); findViewById(R.id.facebook_btn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Loginwith_FB(); } }); findViewById(R.id.google_btn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Sign_in_with_gmail(); } }); findViewById(R.id.Goback).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onBackPressed(); } }); top_view=findViewById(R.id.top_view); login_title_txt=findViewById(R.id.login_title_txt); login_title_txt.setText("You need a "+getString(R.string.app_name)+"\naccount to Continue"); SpannableString ss = new SpannableString("By signing up, you confirm that you agree to our \n Terms of Use and have read and understood \n our Privacy Policy."); ClickableSpan clickableSpan = new ClickableSpan() { @Override public void onClick(View textView) { Open_Privacy_policy(); } @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(false); } }; ss.setSpan(clickableSpan, 99, ss.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); TextView textView = (TextView) findViewById(R.id.login_terms_condition_txt); textView.setText(ss); textView.setClickable(true); textView.setMovementMethod(LinkMovementMethod.getInstance()); printKeyHash(); } public void Open_Privacy_policy(){ Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(Variables.privacy_policy)); startActivity(browserIntent); } @Override public void onEnterAnimationComplete() { super.onEnterAnimationComplete(); AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f); anim.setDuration(200); top_view.startAnimation(anim); top_view.setVisibility(View.VISIBLE); } @Override public void onBackPressed() { top_view.setVisibility(View.GONE); finish(); overridePendingTransition(R.anim.in_from_top, R.anim.out_from_bottom); } // Bottom two function are related to Fb implementation private CallbackManager mCallbackManager; //facebook implementation public void Loginwith_FB(){ LoginManager.getInstance() .logInWithReadPermissions(Login_A.this, Arrays.asList("public_profile","email")); // initialze the facebook sdk and request to facebook for login FacebookSdk.sdkInitialize(this.getApplicationContext()); mCallbackManager = CallbackManager.Factory.create(); LoginManager.getInstance().registerCallback(mCallbackManager, new FacebookCallback<LoginResult>() { @Override public void onSuccess(LoginResult loginResult) { handleFacebookAccessToken(loginResult.getAccessToken()); Log.d("resp_token",loginResult.getAccessToken()+""); } @Override public void onCancel() { // App code Toast.makeText(Login_A.this, "Login Cancel", Toast.LENGTH_SHORT).show(); } @Override public void onError(FacebookException error) { Log.d("resp",""+error.toString()); Toast.makeText(Login_A.this, "Login Error"+error.toString(), Toast.LENGTH_SHORT).show(); } }); } private void handleFacebookAccessToken(final AccessToken token) { // if user is login then this method will call and // facebook will return us a token which will user for get the info of user AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken()); Log.d("resp_token",token.getToken()+""); mAuth.signInWithCredential(credential) .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { Functions.Show_loader(Login_A.this,false,false); final String id = Profile.getCurrentProfile().getId(); GraphRequest request = GraphRequest.newMeRequest(token, new GraphRequest.GraphJSONObjectCallback() { @Override public void onCompleted(JSONObject user, GraphResponse graphResponse) { Functions.cancel_loader(); Log.d("resp",user.toString()); //after get the info of user we will pass to function which will store the info in our server String fname=""+user.optString("first_name"); String lname=""+user.optString("last_name"); if(fname.equals("") || fname.equals("null")) fname=getResources().getString(R.string.app_name); if(lname.equals("") || lname.equals("null")) lname=""; Call_Api_For_Signup(""+id,fname ,lname, "https://graph.facebook.com/"+id+"/picture?width=500&width=500", "facebook"); } }); // here is the request to facebook sdk for which type of info we have required Bundle parameters = new Bundle(); parameters.putString("fields", "last_name,first_name,email"); request.setParameters(parameters); request.executeAsync(); } else { Functions.cancel_loader(); Toast.makeText(Login_A.this, "Authentication failed.", Toast.LENGTH_SHORT).show(); } } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // Pass the activity result back to the Facebook SDK if(requestCode==123){ Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data); handleSignInResult(task); } else if(mCallbackManager!=null) mCallbackManager.onActivityResult(requestCode, resultCode, data); } //google Implementation GoogleSignInClient mGoogleSignInClient; public void Sign_in_with_gmail(){ GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestEmail() .build(); mGoogleSignInClient = GoogleSignIn.getClient(this, gso); GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(Login_A.this); if (account != null) { String id=account.getId(); String fname=""+account.getGivenName(); String lname=""+account.getFamilyName(); String pic_url; if(account.getPhotoUrl()!=null) { pic_url = account.getPhotoUrl().toString(); }else { pic_url="null"; } if(fname.equals("") || fname.equals("null")) fname=getResources().getString(R.string.app_name); if(lname.equals("") || lname.equals("null")) lname="User"; Call_Api_For_Signup(id,fname,lname,pic_url,"gmail"); } else { Intent signInIntent = mGoogleSignInClient.getSignInIntent(); startActivityForResult(signInIntent, 123); } } //Relate to google login private void handleSignInResult(Task<GoogleSignInAccount> completedTask) { try { GoogleSignInAccount account = completedTask.getResult(ApiException.class); if (account != null) { String id=account.getId(); String fname=""+account.getGivenName(); String lname=""+account.getFamilyName(); // if we do not get the picture of user then we will use default profile picture String pic_url; if(account.getPhotoUrl()!=null) { pic_url = account.getPhotoUrl().toString(); }else { pic_url="null"; } if(fname.equals("") || fname.equals("null")) fname=getResources().getString(R.string.app_name); if(lname.equals("") || lname.equals("null")) lname="User"; Call_Api_For_Signup(id,fname,lname,pic_url,"gmail"); } } catch (ApiException e) { Log.w("Error message", "signInResult:failed code=" + e.getStatusCode()); } } // this function call an Api for Signin private void Call_Api_For_Signup(String id, String f_name, String l_name, String picture, String singnup_type) { PackageInfo packageInfo = null; try { packageInfo =getPackageManager().getPackageInfo(getPackageName(), 0); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } String appversion=packageInfo.versionName; JSONObject parameters = new JSONObject(); try { parameters.put("fb_id", id); parameters.put("first_name",""+f_name); parameters.put("last_name", ""+l_name); parameters.put("profile_pic",picture); parameters.put("gender","m"); parameters.put("version",appversion); parameters.put("signup_type",singnup_type); parameters.put("device",Variables.device); } catch (JSONException e) { e.printStackTrace(); } Functions.Show_loader(this,false,false); ApiRequest.Call_Api(this, Variables.SignUp, parameters, new Callback() { @Override public void Responce(String resp) { Functions.cancel_loader(); Parse_signup_data(resp); } }); } // if the signup successfull then this method will call and it store the user info in local public void Parse_signup_data(String loginData){ try { JSONObject jsonObject=new JSONObject(loginData); String code=jsonObject.optString("code"); if(code.equals("200")){ JSONArray jsonArray=jsonObject.getJSONArray("msg"); JSONObject userdata = jsonArray.getJSONObject(0); SharedPreferences.Editor editor=sharedPreferences.edit(); editor.putString(Variables.u_id,userdata.optString("fb_id")); editor.putString(Variables.f_name,userdata.optString("first_name")); editor.putString(Variables.l_name,userdata.optString("last_name")); editor.putString(Variables.u_name,userdata.optString("username")); editor.putString(Variables.gender,userdata.optString("gender")); editor.putString(Variables.u_pic,userdata.optString("profile_pic")); editor.putString(Variables.api_token,userdata.optString("tokon")); editor.putBoolean(Variables.islogin,true); editor.commit(); Variables.sharedPreferences=getSharedPreferences(Variables.pref_name,MODE_PRIVATE); Variables.user_id=Variables.sharedPreferences.getString(Variables.u_id,""); Variables.Reload_my_videos=true; Variables.Reload_my_notification=true; top_view.setVisibility(View.GONE); finish(); startActivity(new Intent(this, MainMenuActivity.class)); }else { Toast.makeText(this, ""+jsonObject.optString("msg"), Toast.LENGTH_SHORT).show(); } } catch (JSONException e) { e.printStackTrace(); } } // this function will print the keyhash of your project // which is very helpfull during Fb login implementation public void printKeyHash() { try { PackageInfo info = getPackageManager().getPackageInfo(getPackageName() , PackageManager.GET_SIGNATURES); for(Signature signature:info.signatures) { MessageDigest md = MessageDigest.getInstance("SHA"); md.update(signature.toByteArray()); Log.i("keyhash" , Base64.encodeToString(md.digest(), Base64.DEFAULT)); } } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } } 。规则语言只能使用有限的一组类型。您可以在tutorial处查看文档。

例如,我们可以通过在声明中使用Array来解决此限制:

List

现在我们可以使用来检查适当的角色

String

,因为{'roles': 'ROLE_ADMIN,ROLE_USER'} 支持".read": "auth.token.roles.contains('ROLE_USER') || auth.token.rules.contains('ROLE_ADMIN')" 方法。正如您在评论中建议的那样,另一种选择是设置诸如

String

然后检查规则中的角色

contains

旁注:我们可以在Firestore安全规则中使用{'role_admin': true, 'role_user': true} ".read": "auth.token.role_admin == true || auth.token.role_user == true" 运算符,请参见https://firebase.google.com/docs/reference/security/database处的文档。